Monday, April 21, 2014

Integrating the Beaglebone and DigiX - Part 2 of 2

This is the second part of my adventures with the Digix! In this blog, I will describe the software and the slightly "hackified" way I got the DigiX and my Beaglebone to talk to one another.

The software is all posted to GitHub here:

https://github.com/torchris/digixbeagle

If you missed the first section of this, check it out here.

Hardware setup

 

Here is a diagram of the breadboard I did up in Fritzing. Note that I have used an Arduino Due instead of the DigiX because there is not yet a DigiX image for Fritzing and the Dues is a similar size and shape.



This is very straightforward and there are many good articles on using the DS18B20 OneWire sensor with the Arduino. Here is an excellent article by Simon Tushev. There are also two LEDs. One shows when requests are being processed and the other shows how a digital output on the DigiX can be controlled by the web page off the BeagleBone.

Server side


The server code for this running on the BeagleBone is a basic Express site modified to use Socket.io and using the Request library to get information from the DigiX. The server then uses Socket.io to push the out to a browser. It also takes input from the browser and sends it back over to the DigiX.

The one thing to note here, which I should have remembered from the last time around is that you can't use the Express template with Socket.io right off the bat. This post from Stackoverflow explains the situation. Just to extract the most salient bit:
Express 3 requires that you instantiate a http.Server to attach socket.io to first:

meaning - (1) you must create a server instance:

var app = express();
var http = require('http').createServer(app);
 
(2) couple it with the socket.io:

var io = require('socket.io');
io.listen(http);
 
and ONLY THEN - (3) make the server listen:

http.listen(8080);
 
make sure you keep this order!
I must tattoo  that on my forehead so I don't waste time with Socket.io not working!!

Anyway, the App.js code is basically all stock except for the change above and this function that gets the info from the DigiX using web page calls and the Request library (more on that later on) and then does some simple text processing to extract the temp.:

function getData() {
    request({
        uri: "http://192.168.1.7:3010/temp"
    }, function(error, response, body) {
        if (error){
            //If there is an error or the DigiX is unreachable, set the value to an error.
            console.log("Error: DigiX not available");
            temp = "DigiX not available";
            }
        else if (!error && response.statusCode == 200) {
            var n = body.search("TEMP:");
            temp = body.substr((n + 6), 5);
            temp = temp.trim();
            console.log("Current temp reading: " + temp);
        }
    });
    setTimeout(getData, 10000);
}
getData();



The other main part of App.js is the Socket.io section:

io.sockets.on('connection', function(socket) {
    console.log('A new user connected!');
    console.log("Sending over initial LED state: " + ledStatOn);
    socket.broadcast.emit('ledStatOn', ledStatOn);
    setInterval(function() {
        socket.broadcast.emit('tempData', temp);
        console.log("Temp sent over sockets to client; " + temp);
    }, 10000);
    socket.on('ledStatOn', function(ledStatOn) {
        if (ledStatOn == "ledOn") {
            console.log('LED data = ' + ledStatOn);
            request({
                uri: "http://192.168.1.7:3010/on"
            }, function(error, response, body) {
                if (!error && response.statusCode == 200) {
                    var x = body.search("ledCmded:");
                    ledOnStat = body.substr((x + 10), 2);
                    ledOnStat = ledOnStat.trim();
                    console.log("LED status is " + ledOnStat);
                    socket.emit('ledStatOn', ledOnStat);
                }
            });
        }
        else if (ledStatOn === "ledOff") {
            console.log('LED data = ' + ledStatOn);
            request({
                uri: "http://192.168.1.7:3010/off"
            }, function(error, response, body) {
                if (!error && response.statusCode == 200) {
                    var x = body.search("ledCmded:");
                    ledOnStat = body.substr((x + 10), 3);
                    ledOnStat = ledOnStat.trim();
                    console.log("LED status is " + ledOnStat);
                    socket.emit('ledStatOn', ledOnStat);
                }
            });
        }
    });

});





When the Socket.io connection is established, this sends over the temerature info from the DS18S20to be displayed on the browser. As well, when commands to turn on or off the LED come over from the browser via Socket.io, the Request library is used to trigger these actions on the DigiX.

Browser side

 

The web interface for this is very, very simple. Just a basic Jade-generated page:


Here is the JavaScript code for the page:

var socket = io.connect();



$(document).ready(function() {

 socket.on('ledStatOn', function(ledStatOn) {
        console.log('Received LED status ' + ledStatOn);
    });   
    
$("input:radio[name=ledStat]").click(function() {
    var val = $('input:radio[name=ledStat]:checked').val();
    console.log(val);
        socket.emit('ledStatOn', val);
});

    
    socket.on('tempData', function(tempData) {
        $('#logger').text('Web server connected.');
        $('#logger').css('color', 'green');
        console.log("Server Connected");
        console.log(tempData);
        $('#tempData').html(tempData);
        socket.on('disconnect', function() {
            // visually disconnect
            $('#logger').text('Web server disconnected.');
            $('#logger').css('color', 'red');
        });
    
    });
});





This reuses the server connected/disconnected status used in the previous project. It also uses "socket.emit" to send over to teh server the user selection for the digital output on the DigiX.

Index.jade is also pretty simple - just the DIVs to write the info into and the radio buttons for the LED:

extends layout

block content

  h1= title
  p Welcome to #{title}
  #logger
  br
  p Curent temperature from DigiX:
  b#tempData
  br
  br
  input(type='radio', name='ledStat', value='ledOff', checked='CHECKED')
  | LED Off
  input(type='radio', name='ledStat', value='ledOn')
  | LED On



DigiX

 

This is where it maybe gets a bit silly, but it does work. As I explained in my last post, I couldn't figure out how to get Socket.io or even good-old Websockets (which I used four years ago with this project in PHP). I am sure someone with more brains or patience could easily come up with an answer, but I got impatient to get this going, so I came up with this solution.

I noticed in the DigiX webserver demo application that the DigiX was parsing the value of the page requested (of course it would have to) and I figured that with different pages being called, the sketch could execute different Arduino commands. In other words, to get the application to do something on the on the DigiX, I just called different web addresses. Here is what this sketch implements. When I call these URLs, I get the following responses:
  • http://192.168.1.7/on = turn ON the LED at digital output 8
  • http://192.168.1.7/off = turn OFF the LED at digital output 8
  • http://192.168.1.7/temp = print a simple webpage with the DS18B20 OneWire temp reading
Nice and simple! Also easy to troubleshoot because I can just use any browser to navigate to those pages and see the results. Is this a basic RESTful API? (I use the LED on pin 9 to show when a web request is being processed.)

Here is the code:

#include 
#include 
#include 
DigiFi wifi;
int ledCmded = 8;
int ledStat = 9;
#define ONE_WIRE_BUS 10

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

void setup()
{
  pinMode(ledCmded, OUTPUT);
  pinMode(ledStat, OUTPUT);
  digitalWrite(ledCmded, LOW);
  digitalWrite(ledStat, LOW);
  Serial.begin(9600);
  wifi.begin(9600);
  sensors.begin();
  //DigiX trick - since we are on serial over USB wait for character to be entered in serial terminal
  while (!Serial.available()) {
    Serial.println("Enter any key to begin");
    delay(1000);
  }

  Serial.println("Starting");

  while (wifi.ready() != 1)
  {
    Serial.println("Error connecting to network");
    delay(15000);
  }

  Serial.println("Connected to wifi!");
  Serial.print("Server running at: ");
  String address = wifi.server(3010);//sets up server and returns IP
  Serial.println(address);

  //  wifi.close();
}

void loop()
{

  if ( wifi.serverRequest()) {
    Serial.print("Request for: ");
    Serial.println(wifi.serverRequestPath());
    if (wifi.serverRequestPath() == "/off") {
      digitalWrite(ledStat, HIGH);
      digitalWrite(ledCmded, LOW);
      Serial.println("ledCmded off");
      wifi.serverResponse("

ledCmded: OFF

");       digitalWrite(ledStat, LOW);     }     else if (wifi.serverRequestPath() == "/on") {       digitalWrite(ledStat, HIGH);       digitalWrite(ledCmded, HIGH);       Serial.println("ledCmded on");       wifi.serverResponse("

ledCmded: ON

");       digitalWrite(ledStat, LOW);     } else if (wifi.serverRequestPath() == "/temp") {       digitalWrite(ledStat, HIGH);       float gotTemp;       gotTemp = getTemp();       Serial.print("Temp =  ");       Serial.println(gotTemp);       wifi.println("HTTP/1.1 200 OK");       wifi.println("Content-Type: text/html");       wifi.println("Connection: close");  // the connection will be closed after completion of the response       wifi.println();       wifi.println("");       wifi.print("

TEMP: ");       wifi.print(gotTemp);       wifi.print("

");       digitalWrite(ledStat, LOW);     }     else {       wifi.serverResponse("

Nothing doing

"); //defaults to 200     }   }   delay(10); } float getTemp() {   float currTemp;   sensors.requestTemperatures(); // Send the command to get temperatures   currTemp = sensors.getTempCByIndex(0);   Serial.println(currTemp); // Why "byIndex"? You can have more than one IC on the same bus. 0 refers to the first IC on the wire   return currTemp; }


This works as an easy way to get the kind of interaction I wanted for this project, which is just to exchange some simple sensor info in a non-realtime way and test a Server -> Arduino digital output. However, this has some obvious limitations. It would be hard, for instance, to exchange analog information from the server to the Arduino (for instance, to dim an LED using PWM). You could perhaps have the BeagleBone server send over a series of URLs like "http://192.168.1.7/analog789" where "789" is the analog value and the Arduino code parses that out of the request and then sets the PWM value, but I doubt you could do this more than a few times per minute.  In other words, this is a nice easy way to get this project working, but I can't see how you could do something like my earlier project where I exchanged realtime movement info over the network (see this project).

That's it for now. Next up will be to include this DigiX sensor data in my previous code so I start to have a real "sensor network". Eventually, I want to use the mesh networking off of the DigiX to get sensor data from several other Arduinos (hopefully the tiny DigiSparks if I can make them work).



Friday, April 11, 2014

Getting started with the DigiX - Part 1 of 2

This is the first part of my project to integrate the Digistump DigiX board with the BeagleBone. In this post, I'll provide a review of the DigiX and some of the good and less good points about working with it. The next section will describe in more detail the software and process I used to build the project.

First, for your multimedia viewing pleasure,  here is a quick video tour of this fairly simple project:


Introducing the DigiX

The DigiX is produced by DigiStump which was founded in 2012 by Mr. Erik Kettenburg in the Seattle area. I first became aware of them when I backed his successful Kickstarter for the DigiSpark which is a super-small $9 Arduino development board  - I will include them in a later part of my monitoring system. A few months after the DigiSpark shipped, I saw the Kickstarter come up for the DigiX and decided to go for it.

The specs on the DigiX are, frankly over the top! This is the Monster Killer Arduino of all Time (at least for now) - check it out:


In form factor, it looks a bit similar to the Arduino Due, but it has a double row of IO pins down the one side whereas the Due only has a single row.

This is a beast!! As well it has BOTH on-board wifi and mesh networking via the popular, low-cost nRF24L01+ wireless module and and a micro SD card slot. All this for only $69.95 USD!

 That being said, given that the BeagleBone Black is only $44.95 and the RaspBerry Pi is only $39.95 (prices from SparkFun) and both of them run full up Linux. Is it worth the extra money to have the DigiX? Here are a few thoughts on pros and cons...

Pros

  1. Rather than having to wrestle with the complexity of managing GPIO in the Linux environment, the DigiX supports the usual Arduino IDE and it's familiar interface. If you're used to stock Arduinos, then there is no ramp up.
  2. The onboard wifi works as advertised and is a breeze to configure according to the DigiX Wiki article (check it out here).
  3. There are more IO pins than any reasonable person could ever use and there is loads of program memory space (524,288 bytes).
  4. There is a good range of sample programs that exercise the functionality of the board and they all seem to compile and work right away. I was amazed that I had a simple web server up and running in about 15 minutes!
  5. I haven't tried the mesh networking yet, but I am assuming it works and it's awesome to have it on the same board.
  6. This board is a 3.3 VDC board (like the other newer Arduinos), but Digistump sells a level shifting shield so you can reuse older 5 VDC shields, which is very useful if you're an old-timer like me.
  7. There is a reasonable amount of documentation and active forums for help.

Cons


The main draw back is is it not quite a stock Arduino and a couple of compromises have been made to keep the board affordable. Let's look at two things I have found...

Wifi

The wifi module on the DigiX is not the same as on the Arduino Yun. The Yun uses the AR9331 while the DigiX uses an Atheros Silicon based embedded UART/WiFi module(source here). This means the DigiFi wifi library is mostly compatible with the Arduino Ethernet library, but not 100%.

Consequently, for the project I just completed, I wanted to use Socket.io or websockets to exchange information between my Beaglebone and the DigiX. I tried various sockets libraries and just could not find a way to make them work with the DigiX! I posted a thread to the DigiX forums, but, since they're somewhat less active than the Arduino boards, I so far haven't found a solution. You will see in my upcoming project that the solution I found works, but is definitely "suboptimal" from a design standpoint.


USB Serial and programming

Mr. Kettenburg provides a good technical description of what happens with USB serial on the DigiX and Due boards here. You can read the detailed explanation, but what it boils down to is that the Arduino Due has two USB ports - one for programming and one for native USB - while the DigiX has only one USB port which has to work for programming and USB. Unfortunately, on the DigiX it seems to sometimes be a coin toss as to which is going to be available when you boot up the board, which caused me some headaches at first.

When I was first using the board, it would do something like this when doing programming:
  1. The Arduino IDE is set on COM16 and you hit the upload button in the IDE.
  2. The program compiles and if there are no errors, then it goes to upload and says "The COM port isn't available".
  3. Restart the DigiX and if you are lucky it comes up with COM15.
  4. While on COM15, hit the upload button and it goes through and programs the board.
  5. However when you go to the IDE to select the serial port for the serial monitor, it is back to COM16!
This could be a huge hassle sometimes requiring multiple restarts of the board and plugging and unplugging the USB until the right port came up. As well, the IDE serial monitor would sometimes not fire up and that required more restarts.

At the moment, I seem to have solved this and it is working smoothly. What I did was follow this advice from the DigiX wiki:

If the COM port isn't showing in the Arduino IDE - unplug and replug the board. If that doesn't work - while plugged in, hold down the erase button on the board for a moment and then unplug and replug - you may then have to select it from the com port menu as it may be on a different port - but it is a sure way to get it to respond even if your sketch crashed the USB stack. 

At the same time as doing this, I moved it to another USB port on my system and Windows reloaded the drivers. It works normally now, but it leaves me concerned it will stop working again in the future. I guess I shouldn't complain because it is a bit of an RTFM situation, but perhaps future versions of the board could have the two port solution like the Due or maybe a jumper block to put the USB port into one mode or the other to avoid this confusion?

Summary


Despite the minor hassles I would still say the DigiX is a great board and worth the money. It gives you loads of room to grow and build very sophisticated networked projects. If you just want something relatively simple to take some digital/analog inputs and then do some digital.analog outputs, then this board may be too much and you should look at some of the simpler Arduinos on the market.

As the "Internet of Things" movement develops, it will be de rigueur to be able to to hook your embedded project to the Internet and to other devices via either mesh or Bluetooth Low Energy. The DigiX has you covered with wifi and mesh on board and while it is more expensive than some boards, you save by having all the connectivity integrated and not having to get separate shields and so on.

Nest up, the software the drives this project!