Wednesday, May 14, 2014

Bringing it all together!

Over the last four blog posts, I have been working step-by-step on creating a basic sensor network in which my BeagleBone takes information from my BlueLine Innovations PowerCost meter and my DigiX Arduino and presents it in a single web interface. Finally, after around three months, I have this working at a level I feel I can write up.

The source code is available on GitHub here.

The system diagram for this project has now expanded to include the new components:


To see how the DigiX is connected to the OneWire sensor, see my previous post here.

Here is a "family portrait" of the hardware on my desk:



I have also updated the layout of the web interface in Jade to move a few elements off to a sidebar:




Here is the code...

App.js


This takes the code I worked up in the last few projects and wraps into one package. Check out GitHub for the full source, but here are a few of the highlights.

The "getData" function has been updated to include two different Request functions - one for the power and outside temperature from the BlueLine Innovations PowerCost gateway and the other from the DigiX OneWire sensor.


function getData() {
    request({
    uri: "http://192.168.1.33/pcmconfig.htm"
    }, function(error, response, body) {
        if (!error && response.statusCode == 200) {
            var n = body.search("Present Demand");
            usage = body.substr((n + 44), 5);
            usage = usage.trim();
            console.log("Power Usage from PowerCost" + usage);
            var n2 = body.search("Sensor Temp");
            temp = body.substr((n2 + 40), 3);
            temp = temp.trim();
            temp = temp - 2;
            console.log("Temp from PowerCost:" + temp);
        }
    });
    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");
            tempDigiX = 19.99;
            DigiXAvailable = "FALSE";
            console.log("DigiX status: " + DigiXAvailable);
        }
        else if (!error && response.statusCode == 200) {
            var n = body.search("TEMP:");
            tempDigiX = body.substr((n + 6), 5);
            tempDigiX = tempDigiX.trim();
            console.log("Current temp reading on DigiX: " + tempDigiX);
            DigiXAvailable = "TRUE";
            console.log("DigiX status: " + DigiXAvailable);
        }
    });
    var readingInfo = new Readings({
        temp2: temp,
        usage2: usage,
        DigiTemp: tempDigiX
    });
    readingInfo.save(function(err, readingInfo) {
        if (err) return console.error(err);
        console.dir(readingInfo);
    });
}

//run the above function

setInterval(getData, (process.env.REFRESHINT * 1000));




(See my previous post here on the slightly "hacky" way I get the temp info off of the DigiX.)

The info is them popped into the "readingInfo" object and then into the TingoDB database. Then a 'setInterval" runs the GetData function on the timing set by the REFRESHINT environment variable. If the Request function for the DigiX can't connect then it sets the DigiXAvailable variable to FALSE and also sets the temperature to a nominal "19.99". I played around with various mechanisms to test and flag that the link to the DigiX was up or down and this was the best I could come up with for now. It does have a lot of lag since it won't set the unavailable status on the web page until it has run through a full three minute cycle.

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

io.sockets.on('connection', function(socket) {
    console.log('A new user connected!');
    Readings.find({}, {}, {
    sort: {
        'time': -1
    },
            limit: process.env.SAMPLES
}, function(err, readings) {
            socket.broadcast.emit('readingsData', readings);
            console.log(process.env.SAMPLES + ' readings sent over');
            console.log('DigiX Status sent over socket = ' + DigiXAvailable);
            socket.broadcast.emit('digiXStatus', DigiXAvailable);
        });
    setInterval(function() {
        Readings.find({}, {}, {
            sort: {
                'time': -1
            },
            limit: process.env.SAMPLES
        }, function(err, readings) {
            socket.broadcast.emit('readingsData', readings);
            console.log(process.env.SAMPLES + ' readings sent over');
            console.log('DigiX Status sent over socket = ' + DigiXAvailable);
            socket.broadcast.emit('digiXStatus', DigiXAvailable);
        });
    }, (process.env.REFRESHINT * 1000));
    socket.on('sampleInput', function(sampleInputSetting) {
        console.log('setting data = ' + sampleInputSetting);
        process.env.SAMPLES = sampleInputSetting;
        socket.broadcast.emit('sampleSetting', sampleInputSetting);
        console.log('Sending sample rate back out');
    });
    socket.on('refreshInput', function(refreshInputSetting) {
        console.log('setting data = ' + refreshInputSetting);
        process.env.REFRESHINT = refreshInputSetting;
        socket.broadcast.emit('refreshSetting', refreshInputSetting);
        console.log('Sending refresh rate back out');
        Readings.find({}, {}, {
            sort: {
                'time': -1
            },
            limit: process.env.SAMPLES
        }, function(err, readings) {
            socket.broadcast.emit('readingsData', readings);
            socket.emit('readingsData', readings);
        });
    });
});

This is pretty similar to the original Socket.io version of my project (check it out here)it's just that now it also sends over the DigiXAvailable flag to the browser for processing to alert end-users of the status of the DigiX.

I really must start using the "Promises" framework in Node to simplify some of these nested functions!


Main.js

 

 

On the client side, the codes is again pretty similar to the last version of this project, except now I have included this in the Socket.io section:

    socket.on('digiXStatus', function(digiXStatusData) {
       console.log('DigiX Status received: ' + digiXStatusData);
        if (digiXStatusData == "FALSE"){
            $('#DigiStatus').text('Offline');
            $('#DigiStatus').css('color', 'red');
            } else {
            console.log("DigiX online");
            $('#DigiStatus').text('Online');
            $('#DigiStatus').css('color', 'green');
            }
    });    




This will take the DigiXAvaialble flag from the socket (renamed digiXStatusData when it gets over to the browser) and then write info to the "#DigiStatus" DIV on the webpage using jQuery.



Index.Jade

 

I decided to change the layout to put a "status" block and the controls off to the left side to make the layout a bit more horizontal. To do this I created CSS elements in Stylesheet.css for the "Status" on the left side and the "Content" area where the chart data goes:

status {
  position: absolute;
  left: 0;
  width: 15em;
}

#content {
  margin-left: 15em;
}




I then created a DIV in Jade and put the content where it belonged:

extends layout

block content    
  #status
    h2 System Status
    table
      tr
        td
          b DigiX:
        td
          #DigiStatus
      tr
        td 
          b BeagleBone:
        td
          #logger 

    h2 Settings
    p Data to graph: 
       input#dataSampleInput(value= '#{sampleNum}', type='text')
    p Time between refresh (sec)
       input#refreshTimeInput(value= '#{refreshRate}', type='text')

    button#submitBtn Submit

  #content
    h1= title

    #legendholder(style='margin-left: 110px;')
    #placeholder(style='width:700px;height:300px')

    h2 Current Readings
    #tablediv
      table
       thead
         tr 
           td 
             b Timestamp
           td
             b Outside Temp
           td
             b Power usage
           td
             b DigiX Temp
         tbody
           tr
             td
               #currTime
             td
               #currTemp
             td
               #currUsage
             td
               #currDigiX


Jade takes some getting used to, but it does allow you to generate a webpage without many lines of code!

DigiX software



This is identical to my post here - the DigiX serves up information depending on which URL is requested. I chose to not implement the digital On/Off controls in this version, but they would be easy enough to include in the future.

Conclusion and next steps


Well, that's about it for this phase. There are a few things I still need to track down...

  • As I said, there is a lot of lag in alerting on the browser if the DigiX goes offline (which it does occasionally) - basically it goes through at least one of it's 180 second cycles. I played around with a "heartbeat" or "ping" function, but I couldn't quite get it to work.
  • Since the DigiX has an SD card slot, it would be good to be able to buffer readings when there is a network interruption and then send them over once the connection is re-established.
  • It would be good to throw an LCD display onto the DigiX to display the sensor readings. 
  • I need to add a few more sensors onto the DigiX as well - light level and humidity, for instance!

Next up, I want to exercise the mesh radio features of the DigiX. A few months back, I bought a few of these NRF24L01+ radios with serial modules off of eBay (check them out here):
I can hopefully use one of these with one of my regular Arduinos just using serial! Check back in a few weeks on that!