Interface & Application Programming

By Dan Chen, September 12, 2014

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
\
&gt; serialport@1.4.6 install /Users/danchen/node_modules/serialport
&gt; 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

What is going on on the Arduino?

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) &amp; 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, &amp; 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 &lt; 16){   // 60 hz - or use 33 for two cycles
                                     // 50 hz in Europe use 20, or 40

 #else

        while (i &lt; 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 &gt; 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 &gt; 1){      // check to make sure param's are within range
        filterVal = .999;
    }
    else if (filterVal &lt;= 0.0){
        filterVal = 0.001;
    }

    smoothedVal = (data * (1.0 - filterVal)) + (smoothedVal  *  filterVal);
    return smoothedVal;
}








Creating a web page that displays the serial data with node.js

 

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

&lt;!doctype html&gt;
  &lt;html&gt;
  &lt;head&gt;
  &lt;meta charset="UTF-8"&gt;
  &lt;script type="text/javascript" src="jquery-1.11.1.min.js"&gt;&lt;/script&gt;
  &lt;script type="text/javascript"&gt;
  setInterval(function() {
  
  $.ajax({  url: "http://127.0.0.1:8080",  cache: false}).done(function( html ) {
  $( "#results" ).text( html );
  }); 
  
  }, 100 ); // every 1/100 seconds
  
  &lt;/script&gt;
  &lt;/head&gt;
&lt;body&gt;
  &lt;div id="results"&gt;Hey Replace Me With Serial Data&lt;/div&gt;
  &lt;/body&gt;
  &lt;/html&gt;

This code goes to the http://127.0.0.1:8080 and pull the number from the page then display them.

Adding some graphics

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.

&lt;!doctype html&gt;
  &lt;html&gt;
  &lt;head&gt;
  &lt;meta charset="UTF-8"&gt;
  &lt;script type="text/javascript" src="jquery-1.11.1.min.js"&gt;&lt;/script&gt;
  &lt;script type="text/javascript"&gt;
  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
  
  &lt;/script&gt;
  &lt;style&gt;
  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;
  }
  
  
  &lt;/style&gt;
  &lt;/head&gt;
&lt;body&gt;
  &lt;div id="results"&gt;Hey Replace Me With Serial Data&lt;/div&gt;
  &lt;img id="hand" src="images/hand.png"&gt;
  
  
  &lt;/body&gt;
  &lt;/html&gt;

You can download the entire code package here.

MaxMsp + SI1143 Proximity Sensors

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.