loading

Drive a webpage in real-time using Arduino, SensorMonkey and Processing.js

Featured
Picture of Drive a webpage in real-time using Arduino, SensorMonkey and Processing.js
overview.jpg
Remote visualization of real-time sensor data.

This tutorial describes in detail how to use the free SensorMonkey service to push real-time sensor data from an Arduino to a webpage for visualization using Processing.js. No server-side coding or Ethernet shield is required. A standard, run of the mill Arduino will work perfectly. You'll also need a sensor to sample some values. I use an accelerometer, but anything will work (a potentiometer, a gyroscope, a tilt sensor, a temperature sensor, a light sensor etc.). If you don't have a sensor, it's still possible to follow the tutorial by sampling the floating input voltages on the Arduino's analog pins as a (somewhat) crude substitute.

After configuring the Arduino to sample sensor values, I use SensorMonkey to publish the data live over the Internet in real-time (Disclosure: I co-founded the company developing SensorMonkey). Using SensorMonkey, I can access the data from any device connected to the Internet and use it to drive a real-time webpage. Proxies, firewalls and NATs can all be traversed. Best of all, it works with standard Arduino boards (Unos, Duemilanoves etc.) and does not require an Ethernet shield. Instead, I use free software called Bloom to network-enable the Arduino and connect it to SensorMonkey. In this tutorial, I visualize the data using Processing.js.

UPDATE 26-06-2012: Non-Windows Users
As an alternative to Bloom for non-Windows users, I have uploaded a Processing sketch, named SensorMonkeySerialNet, to our GitHub account. This sketch is a serial-to-network proxy that also serves Flash Socket Policy files inline. It can be used instead of Bloom in Step 3 for users running Mac OS or Linux.
 
Remove these adsRemove these ads by Signing Up

Step 1: Gathering Materials

Picture of Gathering Materials
setup.jpg
The following combination of hardware and software is required to complete this tutorial:

Hardware:

- Arduino (I use an Uno but older boards such as a Duemilanove will work fine)
- USB cable to connect Arduino to host computer
- Analog sensor (I use a ADXL335 accelerometer)
- Assorted wires to connect your sensor to the Arduino

Software:

- Arduino development environment (http://www.arduino.cc)
- Free account on SensorMonkey.com (login with your existing Facebook account)
- Bloom (serial port to TCP/IP socket redirector for Microsoft Windows)
- Processing.js

Step 2: Connect Arduino and Upload Sketch

Picture of Connect Arduino and Upload Sketch
If you have not done so already, you should take the time to familiarize yourself with the basic operation of an Arduino by reading the Getting Started guide on the main Arduino website. In particular, make sure you have downloaded and installed the Arduino development environment and that you are able to upload sketches to the board. The Arduino will be assigned a serial port when connected to the host computer. If using Windows, you can determine the assigned serial port by opening Device Manager and expanding the Ports (COM & LPT) section. You should see the Arduino listed underneath (in my case, the Arduino has been assigned to COM8).

I have connected my ADXL335 accelerometer to the Arduino as shown (image taken from http://bildr.org). I am going to sample analog-to-digital (ADC) pins 0, 1 and 2 on the Arduino at regular intervals and write their values to the serial port. To do this, I upload the following sketch to the Arduino's microcontroller using the development environment:


void setup() {
  Serial.begin( 9600 );    // Open the serial port.
}

void loop() {
  unsigned int x = analogRead( 0 );    // Read 10-bit x-axis accelerometer on ADC pin 0.
  unsigned int y = analogRead( 1 );    // Read 10-bit y-axis accelerometer on ADC pin 1.
  unsigned int z = analogRead( 2 );    // Read 10-bit z-axis accelerometer on ADC pin 2.
 
  // Write synchronization bytes to serial port to act as starting markers for each 'packet'.
  Serial.write( 0xA5 );
  Serial.write( 0x5A );
 
  // Write x-axis accelerometer to serial port as 16-bit unsigned integer in big-endian format.
  Serial.write( highByte( x ) );    // Most significant byte (MSB).
  Serial.write( lowByte( x ) );    // Least significant byte (LSB).
 
  // Write y-axis accelerometer to serial port as 16-bit unsigned integer in big-endian format.
  Serial.write( highByte( y ) );
  Serial.write( lowByte( y ) );
 
  // Write z-axis accelerometer to serial port as 16-bit unsigned integer in big-endian format.
  Serial.write( highByte( z ) );
  Serial.write( lowByte( z ) );
 
  delay( 20 );    // Add a delay of 20ms to give a sampling rate of approximately 50Hz.
}


The ADC pins have a 10-bit resolution (0 to 1023 inclusive) so I encode them as 16-bit unsigned integers in big-endian format before sending them over the serial port. Depending on the sensor(s) you are using, you can choose to sample more or less of the ADC pins. In my case, the ADXL335 accelerometer measures acceleration along three orthogonal axes: x, y and z. Hence, I sample the three corresponding ADC pins: 0, 1 and 2 respectively.

Finally, you can alter the sampling rate of the sketch by increasing or decreasing the delay as required. For sensors that do not change very often (e.g. a temperature sensor) you will likely want to increase the delay to sample at a slower rate. Setting it to 100 would sample 10 times per second (or 10Hz) for example.

Step 3: Download and Install Bloom

Picture of Download and Install Bloom
Before I can connect the Arduino to SensorMonkey, I need to map the serial port assigned to the device to a TCP/IP socket. To do this, I download and install Bloom from the SensorMonkey support page.

Bloom is a serial port to TCP/IP socket redirector for Microsoft Windows. It comes with a fairly comprehensive help manual (which I would encourage you to read), but the basic operation is very simple:

- Run Bloom from the Windows Start menu
- Configure serial port settings for the Arduino and choose a TCP/IP port for Bloom to listen on
- Set a polling frequency to (approximately) match the sampling rate of the Arduino's sketch
- Press 'Start'

Bloom will listen for incoming connections on the specified TCP/IP port. When a connection is accepted, Bloom will open the serial port and transfer data back and forth between the TCP/IP socket and the serial port, allowing SensorMonkey to connect to the Arduino as if it were a networked device with an Ethernet shield.

I use the following settings:

- TCP/IP port: 20000
- Polling frequency: 50
- Serial port: COM8
- Baud rate: 9600
- Data bits: 8
- Parity bit: None
- Stop bits: 1
- Flow control: None

The choice of TCP/IP port is arbitrary (you can choose whatever you like, as long as it's in the range 1024 to 49151, inclusive, and not already in use). Also, please bear in mind that your serial port will be different depending on what your Arduino was assigned.

For operating systems other than Windows, you can download an alternative to Bloom (typically referred to as a serial-to-network proxy) from our GitHub account. The sketch, named SensorMonkeySerialNet, runs in Processing on Mac OS and Linux. Please follow the instructions in the project's README file.

Step 4: Login to SensorMonkey

Picture of Login to SensorMonkey
You can login to SensorMonkey using your existing Facebook account by clicking the 'Login with Facebook' button in the top-right corner of the page.

You will be prompted to grant permission for the SensorMonkey application to access your Facebook account. Once you have done so, you will be assigned a personal namespace (a streaming 'sandbox' for your sensors) as well as public and private keys to access your namespace from within a webpage. You will need your public key for Step 6. You can find it by clicking the 'Namespaces' link at the top of the page.

Once logged in, you can access the web-based control panel through the 'Sensors' link at the top of the page. The control panel is where you will connect to the Arduino and publish sensor data live over the Internet.

Step 5: Publish Sensor Data

Picture of Publish Sensor Data
stream-tab.png
After logging into SensorMonkey and opening my control panel, I'm going to add an entry for the Arduino named "My Arduino". By clicking on the newly added entry, I can configure the connection parameters; namely, the IP address and port number where the device can be found.

Recall from Step 3 that I am using Bloom to map the Arduino's serial port to TCP/IP port 20000 on my local machine. So, I enter a port number of 20000 and an IP address of 127.0.0.1 (the local loopback address).

I also need to specify a format description file that tells SensorMonkey how to parse and interpret the data being sent by the Arduino. In Step 2, I presented the sketch used to sample the accelerometer that was compiled and uploaded to the Arduino's microcontroller using the development environment. To match the data sent by the sketch, I use the following format description file:


<bytestream>
    <format endian="big">
        <constant>A5</constant>
        <constant>5A</constant>
        <variable type="u16">Accelerometer X</variable>
        <variable type="u16">Accelerometer Y</variable>
        <variable type="u16">Accelerometer Z</variable>
    </format>
</bytestream>


Note that I have specified big-endian format (<format endian="big">) and have added variables representing the three axes sampled by the accelerometer: x, y and z. The type of these variables is "u16", which is short-hand for 'Unsigned 16-bit Integer'. Many different types of variables are supported; you can find more information on the SensorMonkey support page.

The main point to realize here is that you just need to specify a format description file that matches the data being sent by your Arduino over the serial port. Depending on the sensor(s) that you are using, you may need to add more or less variables to your format description file. Make sure to give them descriptive names so you know what each variable is measuring.

After clicking 'Connect', I navigate to the 'Stream' tab, select the three accelerometer variables, choose a stream type of 'Public', and click 'Publish'. The sensor data is now being streamed live over the Internet as a public stream in my personal namespace.

In the next step, I will write a simple HTML webpage to connect to my namespace, subscribe to my stream, and visualize the data in real-time using Processing.js.

Step 6: Graph Data using Processing.js

Picture of Graph Data using Processing.js
In the final (and best!) part of this tutorial, I'm going to create a simple webpage to view the output from my Arduino that is now being streamed live over the Internet using SensorMonkey (I have downloaded the latest Processing.js library - 1.3.6 at the time of writing - and placed it in the same directory as the webpage). You'll need to edit the code below to match the variables being streamed by your Arduino (unless you have copied my accelerometer setup exactly):

(Important! You must replace YOUR_NAMESPACE and YOUR_PUBLIC_KEY in the code below with those assigned to you when you login to SensorMonkey)

--------------------------------------------------------------------------------
<!DOCTYPE html>
<html>
<head>
    <title>Drive a webpage in real-time using Arduino, SensorMonkey and Processing.js</title>
    <script type="text/javascript" src="http://sensormonkey.eeng.nuim.ie/socket.io/socket.io.js"></script>
    <script type="text/javascript" src="http://sensormonkey.eeng.nuim.ie/js/client.min.js"></script>
    <script type="text/javascript" src="processing-1.3.6.js"></script>
    <style type="text/css">
        .sensor-name {
            text-align: center;
            width: 300px;
        }
        canvas {
            border: 1px solid grey;
        }
    </style>
</head>
<body onload="setTimeout( run, 100 );">
    <div class="sensor-name">Accelerometer X</div>
    <canvas data-processing-sources="Graph.pde" id="AccelX"></canvas>
    <div class="sensor-name">Accelerometer Y</div>
    <canvas data-processing-sources="Graph.pde" id="AccelY"></canvas>
    <div class="sensor-name">Accelerometer Z</div>
    <canvas data-processing-sources="Graph.pde" id="AccelZ"></canvas>
    <script type="text/javascript">
        function run() {
            var accelXGraph = Processing.getInstanceById( "AccelX" );
            var accelYGraph = Processing.getInstanceById( "AccelY" );
            var accelZGraph = Processing.getInstanceById( "AccelZ" );
           
            accelXGraph.setColor( 255, 0, 0, 100 );    // Red.
            accelYGraph.setColor( 0, 128, 0, 100 );    // Green.
            accelZGraph.setColor( 0, 0, 255, 100 );    // Blue.
           
            // 1. Connect to SensorMonkey
            // 2. Join namespace
            // 3. Subscribe to stream
            // 4. Listen for 'publish' and 'bulkPublish' events
           
            var client = new SensorMonkey.Client( "http://sensormonkey.eeng.nuim.ie" );
            client.on( "connect", function() {
                client.joinNamespace( "YOUR_NAMESPACE", "YOUR_PUBLIC_KEY", function( e ) {
                    if( e ) {
                        alert( "Failed to join namespace: " + e );
                        return;
                    }
                   
                    client.subscribeToStream( "/public/My Arduino", function( e ) {
                        if( e ) {
                            alert( "Failed to subscribe to stream: " + e );
                            return;
                        }
                       
                        client.on( "publish", function( name, fields ) {
                            if( name === "/public/My Arduino" ) {
                                accelXGraph.update( fields[ "Accelerometer X" ] );
                                accelYGraph.update( fields[ "Accelerometer Y" ] );
                                accelZGraph.update( fields[ "Accelerometer Z" ] );
                            }
                        } );
                       
                        client.on( "bulkPublish", function( name, fields ) {
                            if( name === "/public/My Arduino" ) {
                                for( var i = 0, len = fields[ "Accelerometer X" ].length; i < len; i++ ) {
                                    accelXGraph.update( fields[ "Accelerometer X" ][ i ] );
                                    accelYGraph.update( fields[ "Accelerometer Y" ][ i ] );
                                    accelZGraph.update( fields[ "Accelerometer Z" ][ i ] );
                                }
                            }
                        } );
                    } );
                } );
               
                client.on( "disconnect", function() {
                    alert( "Client has been disconnected!" );
                } );
            } );
        }
    </script>
</body>
</html>
--------------------------------------------------------------------------------

Without going into too much detail (you can find more information about the JavaScript client API here) the basic workflow is as follows:

- Import client
- Connect to SensorMonkey
- Join namespace
- Subscribe to stream
- Listen for 'publish' and 'bulkPublish' events

To graph the data, I'm using the following Processing.js sketch (save this to a file called Graph.pde and place it in the same directory as the webpage above):

--------------------------------------------------------------------------------
int xPos = 0;       // Horizontal coordinate used to draw the next data point.
int yMin = 0;       // Minimum expected data value.
int yMax = 1023;    // Maximum expected data value.
color c;            // Stroke color used to draw the graph.

// Sets the stroke color used to draw the graph.
void setColor( int r, int g, int b, int a ) {
  c = color( r, g, b, a );
}

void setup() {
  size( 300, 200 );
  frameRate( 50 );
  setColor( 255, 0, 0, 100 );
  drawGrid();
}

void draw() {}    // Empty draw() function.

void drawGrid() {
  int h = height;
  int w = width;
 
  background( 255 );
 
  stroke( 127, 127, 127, 127 );
 
  // Draw horizontal lines.
  line( 0, h / 4, w, h / 4 );
  line( 0, h / 2, w, h / 2 );
  line( 0, h * 3 / 4, w, h * 3 / 4 );
 
  // Draw vertical lines.
  line( w / 4, 0, w / 4, h );
  line( w / 2, 0, w / 2, h );
  line( w * 3 / 4, 0, w * 3 / 4, h );
 
  // Draw labels.
  fill( 0 );
  text( str( yMin ), 5, h - 5 );
  text( str( yMax ), 5, 12 );
}

void update( float data ) {
  // When we reach the edge of the screen, wrap around to the beginning.
  if( xPos >= width ) {
    xPos = 0;
    drawGrid();
  }
 
  // Graph the data point and increment the horizontal coordinate.
  data = map( data, yMin, yMax, 0, height );
  stroke( c );
  line( xPos, height, xPos, height - data );
  xPos++;
}
--------------------------------------------------------------------------------

In your case, depending on the sensor(s) that you are streaming, you may need more or less graphs in your webpage. You can edit the Graph.pde file if you need to increase/decrease the size of the graphs, the range of data values that can be plotted, the frame rate etc. Just remember to include the Graph.pde file once for every variable that you want to plot (inside a <canvas> element) and name them accordingly (e.g. <canvas data-processing-sources="Graph.pde" id="TemperatureSensor"></canvas>). Then, you just need to get a reference to the graph (obtained by calling the Processing.getInstanceById() method) and use the update() function to plot new data points received in the "publish" and "bulkPublish" event handlers.

That's it! I now have an accelerometer driving a webpage in real-time using Arduino, SensorMonkey and Processing.js. I can host the webpage on a public webserver and direct people to view the link on any device with a HTML5 compatible web-browser. Thanks for reading and look out for further instructables showing more advanced use cases and projects in the near future.

I know it is a simple circuit, but I have just got to say: That's a great photo of a nice, clean breadboard and workspace. Well done.

pbhat11 year ago

Im doing a project similar to your description, But Im using android cam as webcam, usin Droid cam app. and Im not usin Pan and tilt, It is just stationary.

Now in my project I have to turn on the webcam depending on PIR sensor output which is interfaced with arduino UNO. Im also using sensor monkey, bloom and justin.TV , how can I achieve it. Please do help me .

prajjujan@gmail.com

Hi I'm a total beginner and I'm trying to follow along with the tutorial, I just have one question, how would the tutorial differ if I'm using an arduino YUN, what steps would I leave out?
piano3332 years ago
You we're right, it is a Frontpage problem. I had a problem with where the processing.js file was located, apparently I needed to have the whole URL in there. Once I did that the connection error went away.

I am now getting the same problem as the user below that was only getting the outline of the graphs. I can view it on Sensor Monkey as a remote sensor without any problems, so I know it is streaming. I assume it is a problem with my HTML code then. I have entered my name and public key in the right place, all the text, and without quotation marks. I am new to HTML, and not sure why I am seeing nothing as a result. I can send you the code I have here if it will help. Thanks for your quick response. I hope I can get this thing working.
amccoy6 (author)  piano3332 years ago
Hi,

If you're still having problems you can send me the code and I can take a look through it for you. It could be one of a number of things.

Either send me a private message through Instructables or mail it to sensor.monkeys@gmail.com if you can.

Sincere apologies for the delay in getting back to you.
piano3332 years ago
I have the same problem. I am using Microsoft Frontpage and when doing the preview mode, it continually gives me the error "'Processing' is undefined." I have downloaded the processing-1.4.1.js file and have it referenced in my program the same way as demonstrated above. Any idea why I keep getting this same error?
amccoy6 (author)  piano3332 years ago
I'm not familiar with Microsoft Frontpage so it may be an issue to do with its 'preview' mode.

I would suggest uploading your files to a webserver (one running on your local machine will do) and testing it out to see if the error message persists.
santoroma172 years ago
I only get the outline of the graphs not the data on the web page? any ideas
amccoy6 (author)  santoroma172 years ago
Hi,

Can you check that you are not receiving any JavaScript errors inside your browser? Can you also verify that you can see your stream in the 'Remote Sensors' tab in the SensorMonkey Control Panel (this will tell you whether your data is being streamed correctly).

If you're not receiving any errors and your data is being streamed correctly then double check that you have entered the correct namespace/streamname details in your HTML viewer page (as described in Step 6). These are unique for every user.
botronics2 years ago
I signed up an account with Sensor Monkey. I'm having no success in graphing data from a picaxe sending decimal numbers with the serial port. Such as "72" for 72
degrees. I got Bloom to connect, baud rate is correct, its just the graph is all missed up. How do I set up the Format Description File to receive decimal numbers from the Picaxe?
amccoy6 (author)  botronics2 years ago
Hi,

SensorMonkey only supports binary data encodings. So, when sending the decimal number 72, for example, you can't send it as two separate characters, i.e. "72" where '7' and '2' are the characters. Instead, you have to encode it as an 8-bit binary number, i.e. a single byte containing a bit sequence of 01001000. You do this by writing the raw value to the serial port directly.

Basically, if you are using the PICAXE function sertxd, make sure you DO NOT put a # symbol in front of the variable or value that you are trying to send. Then, in your format description file, you can use an unsigned 8-bit integer (i.e. u8) to read and graph the data in the SensorMonkey control panel.
Yes, that does work. Thank you.
thegrendel3 years ago
This is an intriguing idea. I have a couple of suggestions to
make this more accessible.

1) It should be possible to create an account on SensorMonkey.com
without Facebook. Facebook-centric is a definite disadvantage.

2) Linux support would be extremely helpful. Bloom or equivalent
is probably not necessary on a Linux machine.
amccoy6 (author)  thegrendel3 years ago
Hi Grendel,

Thanks for the comments and apologies for the belated reply.

In response to point (1), we are currently assessing alternatives to Facebook for user logins.

As regards point (2), I have uploaded a Processing sketch to our GitHub account. The sketch is named SensorMonkeySerialNet and runs on Mac OS and Linux. It's not as full-featured as Bloom, but should enable users to connect their Arduinos to SensorMonkey without needing Windows.
hankenstien3 years ago


Yeah NO facebook


And of course anything Linux is good, mayb it;ll work with wine?
HANKIENSTIEN
rduino.com
anniemagz3 years ago
this would work pretty well with the twittering toilet idea...   ;)