Comm Lab Networked Media Week 6

Servi.js in Node.js

Servi can be used without the IDE just using Node.js itself. In fact, that is what we are doing when we export out of the IDE and run on our servers.

To do this on your local machine you need to do the following:

  1. Install Node.js
  2. In the directory you are going work in via terminal: "npm install servi". This will install the servi.js module from the npm service.
  3. Write a server.js file:
  4. var servi = require('servi');
    var app = new servi(false);
    
    app.port(8080);
    
    app.route('/', requestHandler);
    
    function requestHandler(request) {
        request.respond("Hello World");
    }
    
    app.start();
    			
  5. Finally, execute it by running "node server.js" in terminal

Node.js with Serial Data

One of the benefits of running node on your local machine is so that you can interface with things like the Arduino via Serial. Tom Igoe has created a great tutorial/lab on the ITP Pcomp site which we'll use as a starting point: Node Serial Lab

The first thing we'll do is create an Arduino sketch that will spit out some data via serial. This will be our source and we'll build a mechanism to log this data. The Analog Read Serial Arduino sketch provides a great starting point so go ahead and get that running on your Arduino in the normal way. Make sure that it spits out data via serial monitor.

347
351
344
346
352
346
344
352
			

Next, we'll start our Node.js serve. Create a directory to house your Node project and using terminal, install the serialport node module. Don't forget to "cd" to the directory that you are going to working in.

		
npm install serialport
		

After we have that installed, we can run a quick node.js script that will both test the serialport module as well as list the serial ports that are available. If your Arduino is plugged in, you should see it's port listed. I call this listports.js:

		
var serialport = require("serialport");
var SerialPort = serialport.SerialPort;
 
// list serial ports:
serialport.list(function (err, ports) {
  ports.forEach(function(port) {
    console.log(port.comName);
  });
});
this outputs (on my computer)
/dev/cu.Bluetooth-Serial-1
/dev/cu.Bluetooth-Serial-2
/dev/cu.Bluetooth-Modem
/dev/cu.Bluetooth-PDA-Sync
/dev/cu.usbmodem1421		
		
The last one belongs to my Arduino.

Once that is going, we can start working with the code from the Node Serial Lab which is also on Github here: https://github.com/ITPNYU/physcomp/tree/master/labs2014/Node%20Serial%20Lab. We'll start with the index.js file.

/*
	
	Simple Serial Server
	using servi.js and p5.js
	
	created 19 Sept 2014
	modified 3 Oct 2014
	by Tom Igoe

*/

var serialport = require('serialport'),// include the library
   SerialPort = serialport.SerialPort, // make a local instance of it
   // get port name from the command line:
   portName = process.argv[2]; 
   
var latestData = 0;  				// latest data saved from the serial port

var servi = require('servi');		// include the servi library

var app = new servi(false);		// servi instance
app.port(8080);						// port number to run the server on

// configure the server's behavior:
app.serveFiles("public");			// serve all static HTML files from /public
app.route('/data', sendData);		// route requests for /data to sendData() function
// now that everything is configured, start the server:
app.start();	
   
var myPort = new SerialPort(portName, { 
   baudRate: 9600,
   // look for return and newline at the end of each data packet:
   parser: serialport.parsers.readline("\r\n") 
 });   
 
// these are the definitions for the serial events:
myPort.on('open', showPortOpen);  
myPort.on('data', saveLatestData); 
myPort.on('close', showPortClose);
myPort.on('error', showError);

// these are the functions called when the serial events occur:
function showPortOpen() {
   console.log('port open. Data rate: ' + myPort.options.baudRate);
}

function saveLatestData(data) {
   console.log(data);
   latestData = data;
}

function showPortClose() {
   console.log('port closed.');
}

function showError(error) {
   console.log('Serial port error: ' + error);
}

// ------------------------ Server function
function sendData(request) {	
	// print out the fact that a client HTTP request came in to the server:
	console.log("Got a client request, sending them the data.");
	// respond to the client request with the latest serial string:
   request.respond(latestData);
}
You'll see in the above code that we have a normal servi.js server that responds to requests for /data with the sendData function and you'll see the code for the serialport library. Pay particular attention to the callbacks that are defined:
// these are the definitions for the serial events:
myPort.on('open', showPortOpen);  
myPort.on('data', saveLatestData); 
myPort.on('close', showPortClose);
myPort.on('error', showError);

// these are the functions called when the serial events occur:
function showPortOpen() {
   console.log('port open. Data rate: ' + myPort.options.baudRate);
}

function saveLatestData(data) {
   console.log(data);
   latestData = data;
}

function showPortClose() {
   console.log('port closed.');
}

function showError(error) {
   console.log('Serial port error: ' + error);
}
This is very similar to the routing that we are doing with servi or any normal javascript events. In this case, when there is data on the serial port, the saveLatestData function will be called.

Running this example straight is fun but what could we do to modify it so that the data is persistently stored and accessible later?

Hint: Database

To put the data in a database as it comes in, we'll add our normal servi database calls in. After we create the servi object, we can add a useDatabase line.

var app = new servi(false);		// servi instance
app.port(8080);						// port number to run the server on

// database to hold past sensor data
var database = useDatabase("database");
then in the saveLatestData function, we can insert the sensor data into the database:
function saveLatestData(data) {
	console.log(data);
	latestData = data;

	// get a timestamp   
	var timestamp = new Date().getTime();
   
	// create a JSON object to hold the data
	var dataToSave = {timestamp: timestamp, data: data};
	
	// save the data to the database
	database.add(dataToSave);   
}
then, when the data is requested by an HTTP request: http://localhost:8080/data we can serve it up using JSON:
function sendData(request) {	
	// print out the fact that a client HTTP request came in to the server:
	console.log("Got a client request, sending them the data.");

	// Return 100 records from the database
	database.getAll(100,gotData);    

	function gotData(theData) {
		request.headerType = "text/json";
		request.respond(JSON.stringify(theData));
	}

	// respond to the client request with the latest serial string:
	//request.respond(latestData);
}
which will output something like this to the browser:
[{"timestamp":1412776307108,"data":"341","_id":"000AugVlQldZNmpx"},{"timestamp":1412776318083,"data":"340","_id":"00CYDwJoqG1jcOpM"},{"timestamp":1412776357258,"data":"330","_id":"00DCd6dAYBka5f98"},{"timestamp":1412776353264,"data":"331","_id":"00ofwUtiIR8otCP9"},{"timestamp":1412776321622,"data":"340","_id":"012XOZJb6tzWdM0K"},{"timestamp":1412776320639,"data":"340","_id":"01C9PJc5zPWfRPji"},{"timestamp":1412776315842,"data":"340","_id":"01KHYFJYfNPwAm5l"},{"timestamp":1412776357352,"data":"331","_id":"01kStqIxMewIt2BI"},{"timestamp":1412776260272,"data":"351","_id":"026MtfnkDkQvkIIp"},{"timestamp":1412776299357,"data":"342","_id":"02Ij16AonBsnsdrB"}]

We can also sort and limit data coming out of the database by using a few additional parameters in the getAll function:

Assuming you have a field such as "timestamp" in the objects in your database, you can sort them in a query by formatting the query as such:

	database.getAll(100,{timestamp: 1},gotData);    
This will get 100 records from the database with the timestamp in ascending order (from lowest to highest). In fact, this will get the first 100 records in the database because of the sorting.

If we want them in descending order, we use this query:
	database.getAll(100,{timestamp: -1},gotData);    
This will give us the last 100 records.

Using p5.js, we can pull that data in to visualize it:

	var sensorReadings = loadJSON("/data", showData);

serialexample.zip is a complete example.