Using REST as a command protocol for web-to-serial applications

The address you type into your browser’s address bar is often the location of a particular document on a server. For example, http://tigoe.net/index.html refers to an HTML document living in the main directory of my server, tigoe.net. But URLs can be used to represent more than a file address. They can also be used to set or get the state of web-based application. For example, http://www.mystore.com/item/3045/price could be used to tell the application you want the price of item 3045. If you want the set the price, you could use http://www.mystore.com/item/3045/price/4.99. Web frameworks like Sinatra (for Ruby), Flask (for Python) and Express (for JavaScript through node.js) make it possible for you to build a web server that uses Representational State Transfer, or REST, as the control protocol for your application.

REST is a style of formatting URLs such that the URL itself describes the state of the thing it’s addressing. In the store example above, the URL is a representation of the item in the store (http://www.mystore.com/item/3045/price).  To change the state of the item, you use another URL to represent that change (http://www.mystore.com/item/3045/price/4.99).  At its simplest, REST means organizing your web application so that the URLs provide clear, sensible meaning as to what’s going on. For a more detailed explanation, see Understanding REST or Building Web Services the REST way. In this post, you’ll learn how to use a RESTian scheme as a communications protocol between a microcontroller and a web page using node.js and a little client-side JavaScript in the middle.

This post assumes you understand:

To make this happen you’ll need:

  • An Arduino, and the Arduino IDE
  • An RGB LED. I used this common anode model from Adafruit, but you could make it work with a common cathode LED too, with some modification of the Arduino sketch.
  • An HTML5-capable browser. I used Chrome, but Opera or Firefox or Safari will work too)
  • node.js and the following libraries for node:

To get set up, download the project from my GitHub repository. If you’re familiar with git, just clone the whole NetworkExamples repository. If you’re not familiar with git, then download the .zip file and copy the restToSerial directory to your user directory or desktop or wherever you like to work. Then open a terminal window and change directories into the restToSerial directory you just created. Finally, to install all the necessary libraries for node, type

npm install

The node package manager (npm) will then automatically install the needed libraries.

In the previous two posts, I explained how to use websockets to do connect a serial device and a browser, which is similar to this. Both schemes have their advantages and disadvantages. While websockets provide a dedicated two-way connection between client and server, you don’t always need that. Using this  method gives you a way to address the server without needing any client-side programming. This can be useful if you want to control a web application directly from a browser’s address bar, or if you want to control it from another application entirely, just by making a HTTP request. Since HTTP requests are discrete transfers, it also means a more robust connection between client and server.  If the client loses network connection, it doesn’t have to start from the beginning ; it can just continue requesting state updates for the parts of the system it cares about.

The application you’ll build in this lesson is very simple. You’ll control an RGB LED attached to an Arduino microcontroller through an HTTP call. When you’re done, you’ll be able to set the intensity of the red, green, and blue channels from your browser, or from any other application that can make an HTTP call.

The communications protocol for this application looks like this:

  • /output/r/level – sets the level of the red LED (range from 0 to 100)
  • /output/g/level – sets the level of the green LED (range from 0 to 100)
  • /output/b/level – sets the level of the blue LED (range from 0 to 100)

The full address for your HTTP call might look like this:

http://www.example.com/output/r/34

Although this application doesn’t do anything other than control the LED outputs, you’re using the command “output” at the beginnng of the protocol to determine a unique route for the server. Each route defines a set of responses that the server might return. You’ll  add other routes to serve files, and you could add still other routes to define other commands. The server won’t pass the “output” command on to the Arduino, it’ll just pass on the values to be set.

You can make combinations of colors after the output command. For example, /output/r/45/g/23/b/100 would set the red level to 45, the green level to 23, and the blue level to 100. As long as you have a color letter followed by a level, separated by slashes, you’re good.

The Server Script

The server script is the core of this application. You’re going to write a script that can tell the difference between a URL that represents a static file, like:

http://www.example.com/index.html

or

http://www.example.com/js/myscript.js

and a URL that represents a state of the system, like this:

http://www.example.com/output/r/45

Again, each different string after the server’s address is a different route; the route to /index.html is different from the route to the files in /js, and different from the /output route, which represents the state of the LEDs on the microcontroller board. The former two routes are paths to static files, though, while the latter defines a command protocol for the Arduino.

The Express.js module for node.js makes route differentiation easy. Express.js takes the request URL (for example, http://www.example.com/output/r/34) and gives you the route that follows the site address. You can set up different actions to perform for different routes.

You can configure Express.js to treat a whole directory as a location for static files. In this project,  all of the css files are in a directory called /css, and all the client-side JavaScript files are in a directory called /js. Here’s how you can configure Express to serve those directories statically:

  // configure server to serve static files from /js and /css:
  app.use('/js', express.static(__dirname + '/js'));
  app.use('/css', express.static(__dirname + '/css'));

Since you have only one HTML file, there’s no need to set up a /html directory. You can serve the index file like so:

// respond to web GET requests with the index.html page:
app.get('/', function (request, response) {
  response.sendfile(__dirname + '/index.html');
});

The only other route you care about begins with /output. The HTTP request from the client is parsed by express.js’ get() method into a number of request parameters. The first of these is the remainder of the request URL; that is, everything after /output. That’s the part you care about. The code below takes that remainder of the URL and sends it out the serial port. It also sends it back to the client.

// take anything that begins with /output:
app.get('/output*', function (request, response) {
  // the route is the first parameter of the URL request:
  var brightnessCommand = request.params[0];
  // send it out the serial port:
  myPort.write(brightnessCommand);
  // send an HTTP header to the client:
  response.writeHead(200, {'Content-Type': 'text/html'});
  // send the data and close the connection:
  response.end(brightnessCommand);
});

Try running the restToSerial.js script in the repository to see this in action. To run it, you need to know the name of an available serial port. Use the name of the port that your Arduino’s attached to. On my mac, it’s /dev/tty.usbmodem-411. On a Windows machine, it might be COM6. Type the following in the terminal to start node:

node restToSerial.js portName

In my case, it’s

node restToSerial.js /dev/tty.usbmodem-411

You’ll get a message like this:

opening serial port: /dev/tty.usbmodem-411
Listening for new clients on port 8080

Don’t worry about the fact that you haven’t programmed the Arduino yet; for now you just want to see the server’s response to the client. When the server script is running, open a web browser and type

http://localhost:8080/output/r/45/g/67/b/123

You’ll get a reply like this: /r/45/g/67/b/123 That’s the server sending you back what you sent it. You can change the values for red, green, and blue and get different replies. The server script is also sending those values out the serial port, so now it’s time to write an Arduino sketch to read them.

Stop the server script before working on the Arduino sketch because only one application can control a serial port at a time.

The Arduino Sketch

The Arduino sketch included in the gitHub repository for this project, RGBLEDControl.ino, controls an RGB LED using three PWM pins, pins 9, 10, and 11. It starts by listening for incoming serial data, and filters for the relevant characters, r, g, or b, like so:

if (Serial.available() > 0) {
    int inByte = Serial.read(); 

    // respond to the values 'r', 'g', 'b'.
    // you don't care about any other value:
    switch (inByte) {
    case'r':     // red
      currentPin = redPin; 
      break;
    case 'g':    // green
      currentPin = greenPin; 

      break;
    case 'b':    // blue
      currentPin = bluePin; 
      break;
    }

Given one of those three values, it sets the value of the output pin it will control. When the sketch has a legitimate pin number to set (anything other than 0) it starts looking for a value to use to set the pin, like so:

    // if you have a legitimate pin number,
    // use the parseInt function to listen for a level:
    if (currentPin != 0){
      int brightness = Serial.parseInt();

      // map the result to a level from 0 to 255
      // note: the reversal of the output values is because
      // you're using a common anode LED, and taking one of 
      // the cathodes HIGH actually turns that channel off:
      brightness = map(brightness, 0, 100, 255, 0);

      // set the brightness for this color:
      analogWrite(currentPin, brightness);    
    }
  }

Any characters that come in the serial port once the pin number is chosen are parsed to see if they represent an integer using the Serial.parseInt() function. Anything that’s not an r, g, b or a numeric character is ignored.

Upload the sketch included in the repository to your Arduino. Then open the Serial Monitor and type a few commands to it, like:

/r/45/g/67/b/123

You should see the RGB LED light up accordingly. Now close the Serial Monitor and run the server script again. Then open the same URLs you opened before. You should see the LED lighting up just like when you did it from the Serial Monitor. The server script is just passing that part of the URL through to your Arduino. Neat, isn’t it?

But wait! There’s more!

The Client-Side Script

Run the server script and type in:

http://localhost:8080

You’ll get a page that looks like this:

Try sliding the sliders up and down. They should change the brightness of the LEDs on your Arduino.

The sliders on this page are made using jQuery-ui, an extension of the popular jQuery client-side JavaScript framework, that’s good for making UI elements. If you look at the index.html page included in the project repository, you’ll see DIV elements for each of the sliders and their text values. The CSS in the head of the index page sets the positions of these elements, and the colors of the text elements. The JavaScript making it all happen, though, is in the /js directory. It’s called sliders.js.

In sliders.js, there’s a function that defines each slider that looks like this:

$(function(){
   $('#redSlider').slider({
      orientation:'vertical',
      range: 'min',
      value: '0',
      stop: function( event, ui ) {
         $.get(
            "/output/r/" + ui.value,
            function(data) {
                $("#redLabel").html(ui.value);
            }
         );
      }
   });
});

If you’re not used to JavaScript, this can be hard to read. So here’s a breakdown: the slider() function from jquery-ui defines each slider. You pass it the ID of the DIV element in the HTML document where you want that slider. In this example, it’s the DIV called #redSlider. The one parameter for the slider() function is a JSON object defining the slider. You need to give it a bunch of parameters: the orientation, the range, the initial value, and a function to run when it stops, starts, or moves. In this case, you’re only adding a function for when it stops.

When the slider stops, it calls a function you’re giving it to make a HTTP GET request. This is where you’re going to make the call to set the LED. That GET function also needs some parameters: the URL you want to get from the server (/output/r/ + whatever the slider’s value is) and the function you want to run when the GET request is complete. In this case, when the request is complete, you set the DIV called #redLabel with the value of the slider.

Making a GET request from scripts within a page like this lets you perform multiple exchanges with the server just using HTTP, no websockets required. You can update elements within the page this way without refreshing the whole page. It’s handy because you can check your requests directly from the browser’s address bar, using a URL that’s easy to remember because it’s a representation of what you want to happen. It’s also good because each request is a discrete transaction. Once the request is complete, there’s no connection between the server and client, so if you lose your network connection, both sides have a representation of the state of the system when they were last connected.

Because the requests are coming from the elements within the page, though, you don’t need to refresh the whole page every time something changes. You get an interface that’s quick and responsive to the user and robust in terms of connection between the server and client. See? REST in action!

This entry was posted in arduino/wiring, code, javascript, node.js. Bookmark the permalink.

One Response to Using REST as a command protocol for web-to-serial applications

  1. Pingback: domus « elitnics@Portugal