Working with Node,js
Install node.js
Install node.js on Mac by downloading the installation package here http://nodejs.org.
Install serialport for node.js
For most “standard” use cases (node v0.10.x on mac, linux, windows on a x86 or x64 processor), node-serialport will install nice and easy with a simple (In terminal)
<code>npm install serialport </code> |
Here is the GitHub link for serial port https://github.com/voodootikigod/node-serialport
Once you run the command, you will get something like this
Mac:~ dan$ npm install serialport \ > serialport@1.4.6 install /Users/danchen/node_modules/serialport > node-pre-gyp install --fallback-to-build [serialport] Success: "/Users/dan/node_modules/serialport/build/serialport/v1.4.6/Release/node-v11-darwin-x64/serialport.node" is installed via remote serialport@1.4.6 node_modules/serialport ├── bindings@1.2.1 ├── sf@0.1.7 ├── async@0.9.0 ├── nan@1.3.0 └── optimist@0.6.1 (wordwrap@0.0.2, minimist@0.0.10) Mac:~ dan$ |
To get serial data, I ran this code
//////////////////////////////////////////////////////// // Use the cool library // // git://github.com/voodootikigod/node-serialport.git // // to read the serial port where arduino is sitting. // //////////////////////////////////////////////////////// var SerialPort = require("serialport").SerialPort var serialPort = new SerialPort("/dev/tty.usbserial-AE01DXO0", { baudrate: 9600 }, false); // this is the openImmediately flag [default is true] serialPort.open(function (error) { if ( error ) { console.log('failed to open: '+error); } else { console.log('open'); serialPort.on('data', function(data) { console.log('data received: ' + data); }); serialPort.write("ls\n", function(err, results) { console.log('err ' + err); console.log('results ' + results); }); } }); |
(To figure out your FTDI device ID, in my case tty.usbserial-AE01DXO0, you can type in
% ls -l /dev/tty*
to find your device. It is probably somewhere at very beginning )
Run to code by typing in
% node filename.js
I got some serial date, but not what I wanted.
Port open data received: 3 data received: -82 data received: -64 data received: -54 data received: 14 data received: 100 data received: 10 data received: 0 data received: 100 data received: -34 data received: -2 data received: 7 data received: -27 data received: -63 data received: 3 |
Sensor & Serial Data
I am using the SI1143 Proximity Sensors to get the position data. These proximity sensors are based on the SI1143 chip which includes photo-diodes and driver circuitry for three LEDs. The LEDs are pulsed under software control so lots of light-sensing applications can be addressed in a very flexible manner. This is a 3.3 volt chip and power for the sensor is through the pin marked 3.3V and not through PWR pin. There is no voltage regulator on the chip so it should be powered through the 3.3 volt line when using an Arduino. The chip also includes an ambient light and infrared proximity sensor for light measurements without the LEDs.
The chip senses the position of the object that is placed around it. To run my first serial test, I am only using the X-ases
Positive numbers means the object is on the right, negative number means the object is on the left.
Code
/* .ino * http://moderndevice.com/product/si1143-proximity-sensors/ * Reads the Proximity Sensor and either prints raw LED data or angular data * depending the options chosen in the #define's below * Paul Badger, Gavin Atkinson, Jean-Claude Wippler 2013 */ /* For Arduino users use the following pins for various ports Connect to pins with 10k series resistors (inputs are 3.3V only) Connect Ground. Connect 3.3V line to 3.3 volts (PWR line on sensors are not connected). For JeeNode users, just set the port used JeeNode Port SCL ('duino pin) SDA ('duino pin) 0 A5 A4 1 4 14 (A0) 2 5 15 (A1) 3 6 16 (A2) 4 7 17 (A3) */ //lcd stuff #include #define rxPin 1 // rxPin is immaterial - not used - just make this an unused Arduino pin number #define txPin 5 // JeedNode DIO2 must have PWM / pin 14 is analog pin 0, on a BBB just use a servo cable :), see Reference pinMode SoftwareSerial mySerial = SoftwareSerial(rxPin, txPin); int posnum=0; long testval=0; String pos="?x0"; String pos10="?x"; String lcdline="?y"; int lcdlinenum=0; //sensor stuff const int PORT_FOR_SI114 = 1; // change to the JeeNode port number used, see the pin chart above #include const int samples = 4; // samples for smoothing 1 to 10 seem useful // increase for smoother waveform (with less resolution) float Avect = 0.0; float Bvect = 0.0; float Cvect = 0.0; float Tvect, x, y, angle = 0; // some printing options for experimentation (sketch is about the same) #define SEND_TO_PROCESSING_SKETCH // #define PRINT_RAW_LED_VALUES // prints Raw LED values for debug or experimenting // #define POWERLINE_SAMPLING // samples on an integral of a power line period [eg 1/60 sec] // #define PRINT_AMBIENT_LIGHT_SAMPLING // also samples ambient slight (slightly slower) // good for ambient light experiments, comparing output with ambient unsigned long lastMillis, red, IR1, IR2; PortI2C myBus (PORT_FOR_SI114); PulsePlug pulse (myBus); void setup () { pinMode(txPin, OUTPUT); mySerial.begin(9600); // 9600 baud is chip comm speed Serial.begin(9600); //Serial.println("\n[pulse_demo]"); if (!pulse.isPresent()) { //Serial.print("No SI114x found on Port "); //Serial.println(PORT_FOR_SI114); } Serial.begin(9600); digitalWrite(3, HIGH); pulse.setReg(PulsePlug::HW_KEY, 0x17); // pulse.setReg(PulsePlug::COMMAND, PulsePlug::RESET_Cmd); //Serial.print("PART: "); //Serial.print(pulse.getReg(PulsePlug::PART_ID)); //Serial.print(" REV: "); //Serial.print(pulse.getReg(PulsePlug::REV_ID)); //Serial.print(" SEQ: "); //Serial.println(pulse.getReg(PulsePlug::SEQ_ID)); pulse.setReg(PulsePlug::INT_CFG, 0x03); // turn on interrupts pulse.setReg(PulsePlug::IRQ_ENABLE, 0x10); // turn on interrupt on PS3 pulse.setReg(PulsePlug::IRQ_MODE2, 0x01); // interrupt on ps3 measurement pulse.setReg(PulsePlug::MEAS_RATE, 0x84); // see datasheet pulse.setReg(PulsePlug::ALS_RATE, 0x08); // see datasheet pulse.setReg(PulsePlug::PS_RATE, 0x08); // see datasheet pulse.setReg(PulsePlug::PS_LED21, 0x66 ); // LED current for LEDs 1 (red) & 2 (IR1) pulse.setReg(PulsePlug::PS_LED3, 0x06); // LED current for LED 3 (IR2) // Serial.print( "PS_LED21 = "); // Serial.println(pulse.getReg(PulsePlug::PS_LED21), BIN); // Serial.print("CHLIST = "); // Serial.println(pulse.readParam(0x01), BIN); pulse.writeParam(PulsePlug::PARAM_CH_LIST, 0x77); // all measurements on // increasing PARAM_PS_ADC_GAIN will increase the LED on time and ADC window // you will see increase in brightness of visible LED's, ADC output, & noise // datasheet warns not to go beyond 4 because chip or LEDs may be damaged pulse.writeParam(PulsePlug::PARAM_PS_ADC_GAIN, 0x00); pulse.writeParam(PulsePlug::PARAM_PSLED12_SELECT, 0x21); // select LEDs on for readings see datasheet pulse.writeParam(PulsePlug::PARAM_PSLED3_SELECT, 0x04); // 3 only pulse.writeParam(PulsePlug::PARAM_PS1_ADCMUX, 0x03); // PS1 photodiode select pulse.writeParam(PulsePlug::PARAM_PS2_ADCMUX, 0x03); // PS2 photodiode select pulse.writeParam(PulsePlug::PARAM_PS3_ADCMUX, 0x03); // PS3 photodiode select pulse.writeParam(PulsePlug::PARAM_PS_ADC_COUNTER, B01110000); // B01110000 is default pulse.setReg(PulsePlug::COMMAND, PulsePlug::PSALS_AUTO_Cmd); // starts an autonomous read loop // Serial.println(pulse.getReg(PulsePlug::CHIP_STAT), HEX); } void loop(){ unsigned long total=0, start; int i=0; red = 0; IR1 = 0; IR2 = 0; total = 0; start = millis(); #ifdef POWERLINE_SAMPLING while (millis() - start < 16){ // 60 hz - or use 33 for two cycles // 50 hz in Europe use 20, or 40 #else while (i < samples){ #endif #ifdef PRINT_AMBIENT_LIGHT_SAMPLING pulse.fetchData(); #else pulse.fetchLedData(); #endif red += pulse.ps1; IR1 += pulse.ps2; IR2 += pulse.ps3; i++; } red = red / i; // get averages IR1 = IR1 / i; IR2 = IR2 / i; total = red + IR1 + IR2; #ifdef PRINT_AMBIENT_LIGHT_SAMPLING Serial.print(pulse.resp, HEX); // resp Serial.print("\t"); Serial.print(pulse.als_vis); // ambient visible Serial.print("\t"); Serial.print(pulse.als_ir); // ambient IR Serial.print("\t"); #endif #ifdef PRINT_RAW_LED_VALUES Serial.print(red); Serial.print("\t"); Serial.print(IR1); Serial.print("\t"); Serial.print(IR2); Serial.print("\t"); Serial.println((long)total); Serial.print("\t"); #endif #ifdef SEND_TO_PROCESSING_SKETCH /* Add LED values as vectors - treat each vector as a force vector at * 0 deg, 120 deg, 240 deg respectively * parse out x and y components of each vector * y = sin(angle) * Vect , x = cos(angle) * Vect * add vectors then use atan2() to get angle * vector quantity from pythagorean theorem */ Avect = IR1; Bvect = red; Cvect = IR2; // cut off reporting if total reported from LED pulses is less than the ambient measurement // eliminates noise when no signal is present if (total > 900){ //determined empirically, you may need to adjust for your sensor or lighting conditions x = (float)Avect - (.5 * (float)Bvect ) - ( .5 * (float)Cvect); // integer math y = (.866 * (float)Bvect) - (.866 * (float)Cvect); angle = atan2((float)y, (float)x) * 57.296 + 180 ; // convert to degrees and lose the neg values Tvect = (long)sqrt( x * x + y * y); } else{ // report 0's if no signal (object) present above sensor angle = 0; Tvect = 0; total = 900; } //float angle2=angle; //angle2 = ( angle2 + 210 ) % 360; // change some mapping for LED position angle = radians(angle); float x = cos(angle)*10000; float y = Cvect; /* Serial.print(x); Serial.print("\t"); */ //Serial.println(y); if (x==10000.00){ posnum=9; lcdlinenum=3; } else{ x = constrain(x, -8770, 3920); // posnum = map(x, 3920, -8770, 100, 0); posnum = map(x, -8770, 3920, -100, 100); y = constrain(y, 300, 800); // lcdlinenum = map(y, 300, 800, 100, 0); lcdlinenum = map(y, 300, 800, -100, 100); } //Serial.println(posnum); #endif //lcd display //Serial.print(posnum); //Serial.print(" - "); Serial.print(lcdlinenum); delay(200); } // simple smoothing function for future heartbeat detection and processing float smooth(float data, float filterVal, float smoothedVal){ if (filterVal > 1){ // check to make sure param's are within range filterVal = .999; } else if (filterVal <= 0.0){ filterVal = 0.001; } smoothedVal = (data * (1.0 - filterVal)) + (smoothedVal * filterVal); return smoothedVal; } |
I followed this tutorial, under “Your first program”
var http = require('http'); var server = http.createServer(function(req, res) { res.writeHead(200); res.end('Hello Http'); }); server.listen(8080); |
This program doesn’t exit right away. That’s because a node program will always run until it’s certain that no further events are possible. In this case the open http server is the source of events that will keep things going.
Testing the server by going to the following url: http://localhost:8080/.
Now we want to change the out put “Hello Http” from the above example to get serial data that we print earlier. and go the http://localhost:8080/ again on your browser.
Here is the code that I ran with node, I can get data by going to http://127.0.0.1:8080
I can get current sensor reading by refreshing the page.
var mydata; var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); getserialdata(); res.end(mydata); }).listen(8080, '127.0.0.1'); //console.log('Server running at http://127.0.0.1:1337/'); function getserialdata(){ var SerialPort = require("serialport").SerialPort var serialPort = new SerialPort("/dev/tty.usbserial-AE01DXO0", { baudrate: 9600 }, false); // this is the openImmediately flag [default is true] serialPort.open(function (error) { if ( error ) { console.log('failed to open: '+error); } else { console.log('open'); serialPort.on('data', function(data) { console.log('data received: ' + data); mydata=data; }); serialPort.write("ls\n", function(err, results) { //console.log('err ' + err); //console.log('results ' + results); }); } }); } |
Working with Jquery
Make sure you include the Jquery Library
<!doctype html> <html> <head> <meta charset="UTF-8"> <script type="text/javascript" src="jquery-1.11.1.min.js"></script> <script type="text/javascript"> setInterval(function() { $.ajax({ url: "http://127.0.0.1:8080", cache: false}).done(function( html ) { $( "#results" ).text( html ); }); }, 100 ); // every 1/100 seconds </script> </head> <body> <div id="results">Hey Replace Me With Serial Data</div> </body> </html>
This code goes to the http://127.0.0.1:8080 and pull the number from the page then display them.
Disclaimer: I don’t really like cats.
This is the same code as before, but I took out the console.log hooping that it will run faster, but not killing it.
This is the server side script.
var mydata; var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); getserialdata(); res.end(mydata); }).listen(8080, '127.0.0.1'); //console.log('Server running at http://127.0.0.1:1337/'); function getserialdata(){ var SerialPort = require("serialport").SerialPort var serialPort = new SerialPort("/dev/tty.usbserial-AE01DXO0", { baudrate: 9600 }, false); // this is the openImmediately flag [default is true] serialPort.open(function (error) { if ( error ) { //console.log('failed to open: '+error); } else { //console.log('open'); serialPort.on('data', function(data) { //console.log('data received: ' + data);// turn this off hopping that it will run faster mydata=data; }); //serialPort.write("ls\n", function(err, results) { //console.log('err ' + err); //console.log('results ' + results); //}); } }); } |
Here is the new HTML with images + css + js
I am using the position data to control the position of the image.
Here is the HTML or the client side script. This code is using jquery to pull the data from page http://127.0.0.1:8080, which is the sensor data. Jquery pulls this data every 1/100 seconds. I then use .css() Jquery function to map out the position of the hand to pet the cat.
<!doctype html> <html> <head> <meta charset="UTF-8"> <script type="text/javascript" src="jquery-1.11.1.min.js"></script> <script type="text/javascript"> setInterval(function() { /* $.ajax({ url: "http://127.0.0.1:8080", cache: false}).done(function( html ) { $( "#results" ).text( html ); }); */ $.ajax({ url: "http://127.0.0.1:8080", cache: false}).done(function( position ) { //alert (position); // debug $( "#hand" ).css( "left", 500-position+"px"); $( "#results" ).text( position ); }); }, 100 ); // every 1/100 seconds </script> <style> body, html { font-family:Helvetica, sans-serif; margin:0; background-image:url(images/cat.jpg); background-position: center 50px; background-size:1000px; background-repeat:no-repeat; } #hand{ position:fixed; } </style> </head> <body> <div id="results">Hey Replace Me With Serial Data</div> <img id="hand" src="images/hand.png"> </body> </html>
You can download the entire code package here.
Now for something more serious … Using MaxMsp and SI1143 Proximity Sensors, I want to create Minority Report style gesture sensing program to control the play back of the Minority Report … the movie.
The program is still a little buggy, I will need more time to fine tune it.
Here is the Max Patch, I places the video file in the same folder so it can find the file.