Wednesday, March 26, 2014

BeagleBone Node.js project updated with Socket.io

After I finished my last project, I realized I wasn't entirely satisfied with it. While it basically functioned, relying on it doing a whole page refresh every so often just didn't seem "elegant". As well, when you changed the settings in one browser, they didn't change in other browsers, which, again, didn't seem very slick.

I figured I had to learn to use the Socket.io library anyway to eventually communicated with my Arduino DigiX, so why not update the BeagleBone project to "socketize" it? Naturally, this took a bit of a learning curve, but I like the results.

The project hardware setup is exactly the same as my previous post - using the BeagleBone and the BlueLine Innovations PowerCost Monitor. I have also gotten smart and finally signed up for GitHub and here is a link to the complete source code:


Finally, I have done a YouTube overview of the functionality of the project:



In case it isn't clear in the video, here is what the web page looks like:



The software uses all the libraries from the previous version (Request, Flot, jQuery etc.). The only addition is Socket.io, however, I ended up practically rewriting the application from scratch to accommodate the change!

Server side - App.js

 

Some of the elements of App.js from the last version of this project are the same, so I won't go into detail on them such as using the Request library to do the screen scrape and then popping that into the TingoDB/Mongoose database. However, it seems like Socket.io is only "semi compatible" with Express and you need to make several changes from the default Express setup.

Again, there are many, many good tutorials out there by folks who understand this at a deeper level. Below is just my description of how I got this to work. Here are a couple of good Socket.io tutorials I found along the way that may be helpful:



Here is the section of App.js setting up Socket.io, which seems to have to come later in the App.js file than the usual "require" statements at the beginning:

var io = require('socket.io').listen(server);

app.set('port', process.env.PORT || 3008);

io.set('log level', 1); 


In my last version of this project the "app.get" and "app.post" functions did most of the heavy lifting of rendering information over to web page. Now with Socket.io, the "app.post" section is completely gone since the button pushing is now handled by Socket.io rather than the traditional POST method.

The main action is now all within this one big Socket.io loop:

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);
        socket.broadcast.emit('sampleSetting', process.env.SAMPLES);
        socket.broadcast.emit('refreshSetting', process.env.REFRESHINT);
        console.log('Initial data over to browser.');
    });
    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);
        });
    });
    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');
        });
    }, (process.env.REFRESHINT * 1000));
});


At it's heart, when a browser connects and establishes a socket, this sends over an initial chunk of data and then kicks off the setInterval loop. This broadcasts out a chunk of database data to all connected browsers according to the refresh interval environment variable (process.env.REFRESHINT).

When the "submit" button is hit on the website, this opens the the refreshInput and sampleInput sockets and they in turn send over the revised values to the other browsers connected to the server.

Overall, this works fine, but really looks ugly to me and I am sure I'm doing something wrong! I have repeated the database query three times. I think this should be put into a separate function, but then this isn't the way Node.js works. I looked at implementing the Promises framework (more here), but I got lost in it and I decided to just close off where I am. Promises will need to wait until later.

Client side - Main.js


Since the client-side JavaScript had gotten rather complicated, I decided to put it off into a  separate file which I called "main.js" which lives in the public/javascripts folder of the project. This is referenced in the "Layout.jade" file (more about Jade in a minute).

At the top is this to load the Socket.io:

var socket = io.connect();

 Then we have the jQuery function started up and the logic for the Settings button:

$(document).ready(function() {
    console.log('Doc loaded');
    $('#submitBtn').click(function() {
        var sampleInput = document.getElementById("dataSampleInput").value;
        console.log(sampleInput);
        var refreshInput = document.getElementById("refreshTimeInput").value;
        console.log(refreshInput);
        socket.emit('sampleInput', sampleInput);
        socket.emit('refreshInput', refreshInput);
    });


This is some jQuery that activates when the button is pushed, and picks up the values entered into the two fields with Javascript and then the pushes the values back to the server with socket.emit. You can see the "sampleInput" and "refreshInput" values here that end up over at the server side.

Then there are two further sockets that take the settings value back from the server when they are rebroadcast out:

    socket.on('sampleSetting', function(serverSampleSetting) {
        console.log('Received new sample rate ' + serverSampleSetting);
        $('#dataSampleInput').val(serverSampleSetting);
    });
    socket.on('refreshSetting', function(serverRefreshSetting) {
        console.log('Received new refresh rate ' + serverRefreshSetting);
        $('#refreshTimeInput').val(serverRefreshSetting);
    });


This uses jQuery to write these values back to the fields so all browsers show the same settings.

The socket.on 'readingsData' is what receives the data object over from the server when it is sent over according to the refresh interval. There is then basically the same logic as I used in the last project to break down the data object into two separate arrays and then feed that into Flot to generate the graph. Just check out my previous post on how that works.

The "Server connected" and "Server disconnected" status line is an idea I got from James Doyle and this great tutorial on YouTube (GitHub here). It is very simple and works like this. When the main readingsData socket starts up a couple of lines of jQuery write to a DIV called "logger":

        $('#logger').text('Server connected.');
        $('#logger').css('color', 'green');


When the socket disconnects, it changes the message:

        socket.on('disconnect', function() {
            // visually disconnect
            $('#logger').text('Server disconnected.');
            $('#logger').css('color', 'red');

Mr. Doyle used plain JavaScript for his, but I found for whatever reason this didn't work across all browsers, but jQuery does. The only limitation with this is that it will go to "disconnected" if the server is offline and then back to "connected" if it comes back on fairly quickly (let's say 15 min or so), but if the server is down for too long it won't auto-reconnect and you need to reload the web page.  It also won't go to a "disconnect" status if the Ethernet cable is pulled out of the BeagleBone, for instance.

Jade files - Layout.jade and Index.jade

If you check out these files on GitHub, you will see they are much simpler than they were the first time around. I moved the bulk of the scripting into "Main.js" and the original form I used for submission of the settings has been much simplified.

Hopefully this is interesting to all and sundry! Next up I promise to get on with working with the DigiX and maybe I will even manage a flashing LED using Socket.io!!

Saturday, March 1, 2014

Creating a Node.js application on the BeagleBone


Well, as usual, it has taken a log time to get to another post! Hopefully it was worth the wait.

WARNING 1 - I am very much of a beginner with Node.js! Please Google before asking me any questions about the programming on this project. The code in this project more or less works and demonstrates basic concepts, but totally lacks the kind of error handling and security that production code should always incorporate.

WARNING 2 - I am also NOT an expert on the BeagleBone and this project really does not use any of the GPIO features of the board (in fact, the Node.js could be run on Windows as easily as the BB). Anyone wanting to understand the board should first look at Derek Molloy's fantastic blog and YouTube videos!

Motivation and background

 

I first pre-ordered my BeagleBone way back in 2011 when the product was first announced. At the time I was interested in the BeagleBoard and the RaspBerry Pi wasn't yet released and it seemed to have good specs for the cost. When I got it, I booted it up and played around with it a bit, but there wasn't a ton of documentation available yet and I was very busy with work. Lately I've been coming back around to my embedded electronics work and when I pulled it out I found that now there was tons of new material around on the device. Just to be clear, mine is the original "white" BeagleBone not the newer "Black" model.

After updating the Angstrom image and booting it up, I saw that the BB provides it's own on-board IDE which is a locally version of normally cloud-based Cloud9 (more on Cloud9) and runs this new and mysterious thing called "Node.js". I had a small amount of JavaScript knowledge, so I bought a text on Node.js (Sams Tech Yourself Node.js) so I could start puzzling it out.

Rather than plunge into the depths of doing GPIO with the BB right away and introducing too many variables, I decided to basically use the BB as a web server and to come up with a simple goal so I could  try to learn some Node.js and prepare for something more substantial later on. I decided something that would be "simple" to do would be figue out how to screen scrape my BlueLine Innovations Wifi PowerCost Monitor (more details here) to provide a visual indication of my power usage and the outside temp. Since I am already using Plot

Gear and setup

 

No elaborate wiring diagrams or anything here. The equipmentused is:
  • BeagleBone (White, running Angstrom Linux image)
  • BlueLine Innovations PowerCost Monitor™ WiFi Bridge
  • BlueLine Innovations Energy Monitor outside meter sensor

Here are the BeagleBone and the PowerCost Monitor:


Here is the outdoor portion strapped onto my utility power meter. Seems a bit funky, but it has been out there for a few years now.


In this project, the Outdoor monitor communicates with the Wifi gateway over a proprietary protocol and then the Gateway serves up a small web page which looks like this:


The Node.js running on the BeagleBone captures the webpage at regular intervals, stores the temperature and usage data in a Tingo database (more on that later) and then serves up a web page that shows a table of recent results with a graph. Here is the layout:


The Web Page 


This is the  resulting power & temp usage monitor web page. It shows:
  • a graph at the top of power use and temp over time
  • a listing of the data points with their timestamps 
  • a settings area where the number of data samples and the interval between samples can be set.



The code

 

This project makes use of many different Node.js and JavaScript library. Specifically:

  • Node.js - asynchronous, non-blocking, server-side JavaScript
  • Express - web framework for Node.js
  • Jade - HTML templating engine for Node.js
  • TingoDB - This is a "noSQL" database alternative to MongoDB. (I couldn't get Mongo to run on the BeagleBoard Angstrom release, but TingoDB works fine - probably because it is written entirely in JavaScript.)
  • Tungus - This is a driver to allow use of the Mongoose implements mongoose.js driver API.
  • Mongoose - object modelling tool for Mongo (in this case being used with TingoDB via Tungus - say that five times fast!)
  • Request - A simple HTTP client for Node.js
  • Flot - A charting package for jQuery
  • jQuery - JavaScript library for HTML document manipulation
Getting all these elements to work together was a  fair challenge given my complete ignorance at the start of this project!

I won't bother with a complete step-by-step build process here. There are many number of great tutorials on the web that walk through creation of Node.js/Express/Jade websites. Christopher Buecheler has a great one over here, for instance. Suffice it to say, first install Node.js, then use npm to install all the dependencies. If you get error messages with the npm installation, Google them and you will pretty much always find an answer. After that, I used Express to create the basic shell of the website.

This is a comparatively simple, single page Node.js project. Besides the libraries above, there are 4 critical files:
  • app.js
  • layout.jade
  • index.jade
  • style.css

The complete source code files are at the end of the article and I will highlight critical snippets of code to help understand how it all hangs together. Other code in the files (especially app.js) are set up by Express when you create the website.

Once I had the basic Express/Jade site built and included all the dependencies, I ended up with a directory structure that looked like:


App.js notes:

 

App.js is the server code that runs the whole thing. It also initializes the database, sets environment variables, uses the Request library to extract data from the Wifi PowerCost Monitor website and store that in the TingoDB database. Finally, when the gets a "GET" request from a browser, it renders the data on to the Jade engine.

Here are the sections that manage the database. This code links to the dependencies:

var tungus = require('tungus');
var Engine = require('tingodb');
var mongoose = require('mongoose');

This code initializes Mongoose with Tingo and names the database:

var db = mongoose.connect('tingodb://readingsdb');


Then Mongoose connects and prints to the console:

mongoose.connect('tingodb://readingsdb', function (err){
    if (!err) {
        console.log('connected to databse');
    } else {
        throw err;
    }
});


Then we set up the schema for the database:

var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var ReadingsSchema = new Schema({
  time: { type : Date, default: Date.now },
    usage2: Number,
    temp2: Number
    });
var Readings = mongoose.model('Readings', ReadingsSchema);


I decided to set the refresh rate (how often the monitor browser reloads) and the interval time (how long between data samples) via environment variables, which I do here:


process.env['SAMPLES'] = '15';

process.env['REFRESHINT'] = '120';


The screen scrape and data extraction is done by the getData function which uses the Request library to load the PowerCost Monitor webpage into a variable, then uses simple JavaScript string commands to isolate the two values we want. It loads the values into the database created above and used a JavaScript setInterval function to repeat the process however often has been set for the refresh rate.

One minor note is I find I have to subract 2 from the temperature I get from the PowerCost Wifi Monitor to get the correct temperature. It just seems to be a slight calibration issue.


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(usage);
           var n2 = body.search("Sensor Temp");
           temp = body.substr((n2+40),3);
           temp = temp.trim();
           temp = temp - 2;
           console.log(temp);
           var readingInfo = new Readings({
               temp2: temp,
               usage2: usage
            });
        readingInfo.save(function(err, readingInfo){
              if (err) return console.error(err);
            console.dir(readingInfo);
            });
        setTimeout(getData,(process.env.REFRESHINT * 1000));
    }});
}
//run the above function
getData();



Now we have our data and stored it into a database so we need to render it over to Jade. When a GET request is received from a browser, this function is run:

app.get('/', function(req, res){
  Readings.find({}, {}, { sort: { 'time' : -1}, limit: process.env.SAMPLES }, function(err, readings) {
    if (err) return console.error(err);
      res.render('index', 
        { title: 'Power Usage and Temp from PowerCost Monitor',
          refreshRate: process.env.REFRESHINT,
          sampleNum: process.env.SAMPLES,
          readings: readings
              });
           }
          );
       }
    );


This renders the title, the environment variables and an object with the actual temp & power readings over to Jade via the "res.render" function.


If the user wants to change the sample interval or the refresh rate, they enter them in the the "Settings" fields and when you hit "Submit" it triggers the app.post function, whichwill set the refresh rate and sample number environment variables and then re-render the page:

app.post('/', function(req, res){
    process.env['SAMPLES'] = req.param("samples");
    process.env['REFRESHINT'] = req.param("interval");
    console.log('Samples is set to:  ' + process.env.SAMPLES);
    console.log('Refresh is set to:  ' + process.env.REFRESHINT);    
      Readings.find({}, {}, { sort: { 'time' : -1}, limit: process.env.SAMPLES }, function(err, readings) {
    if (err) return console.error(err);
      res.render('index', 
        { title: 'Power Usage and Temp from PowerCost Monitor',
          refreshRate: process.env.REFRESHINT,
          sampleNum: process.env.SAMPLES,
          readings: readings
              });
           }
          );
       });

Finally, app.js contains the code to actually run the webserver, which is just as Express generated it:

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});


Layout.jade

 

Over the course of development, I went back and forth over whether to include the bulk of the client-side JavaScript in the Layout.jade file (essentially the headers of the webpage) or at the end. After reading a few articles (such as this one) , I decided to put the main client-side JS at the end of the page (in this case, the Index.jade file) and it does seem to perform better.

Not really too much to the Layout.jade file, but note this line:

      meta(http-equiv="refresh" content="#{refreshRate}" ) 


It sets the refresh rate based on the environment variable mentioned earlier. I tried to go with having the table and the Flot chart in DIVs and just refreshing the DIVs, but this seemed to not work easily so I will tackle this in the future.

Index.jade

 

This is the other major working part of this project and comes in two parts. First is the Jade portion:

It took me a while to learn how to get the variables to come across properly and to get used to how finicky Jade is about spaces! The coding and hierarchy for the resulting HTML page rely entirely on how you have used spaces to indent, so you have to be very careful! Also note that I have set up the size of the placeholder DIV for the Flot chart. It seems like you need to do this to ensure the chart generates properly.

extends layout

block content    
    h1= title
    
    h2 Graph over time
    br
    #placeholder(style='width:700px;height:300px')
    
    h2 Table of results
    #tablediv
      table
       thead
         tr 
           td 
             b Timestamp
           td
             b Temperature
           td
             b Power usage
         tbody
             each reading in readings
               tr
                 td #{reading.time}
                 td #{reading.temp2}
                 td #{reading.usage2}



The section below is the form for doing the settings. The thing to notice here is that this form also picks up the current values of the environment variables for refresh and interval. The defaults are 15 samples and an interval of 180 seconds.

    h2 Settings
    form#dataSamples(name="dataSamples",method="post",action="/")
        label(for "samples") Data samples to graph:
        input#setSamplesInput(type="text", placeholder="samples", name="samples", value="#{sampleNum}")
        br
        label(for "interval") Time between refreshes (seconds):       
        input#setIntervalInput(type="text",placeholder="interval", name="interval", value="#{refreshRate}")
        br
        button#btnSubmit(type="submit") submit

Then at the bottom of the Index.jade file is the client-side JavaScript which is really what makes everything happen. This points to the jQuery and Flot libraries (which are in the /public folder in the directory tree).

When the page finishes loading, this translates the data object of the database readings from the app.js res.render statement into two separate arrays (one for temp and one for power usage) using the JSON.stringify command so they are ready for plotting. It uses the $.plot function in the Flot library to create the plot. It also sets the labeling and axis formatting as well. (Note that to get the time-based X Axis, you need to include the Flot Time library as well as Flot itself.

    script(src='jquery/dist/cdn/jquery-2.1.0.js')
    script(src='flot/jquery.flot.js')
    script(src='flot/jquery.flot.time.js')
    script.
        window.onload = function(){
           if (window.console)console.log("Executing script");
           var temp_data = [];
           var usage_data = [];
           var reading_data = !{JSON.stringify(readings)};
           for (var i = 0; i < #{sampleNum}; i++)
             temp_data.push([new Date(reading_data[i].time), reading_data[i].temp2]);
           for (var i = 0; i < #{sampleNum}; i++)
             usage_data.push([new Date(reading_data[i].time), reading_data[i].usage2]);
           $.plot($("#placeholder"), [{
               label: "Temperature (C)",
               data: temp_data, 
               },{
               label: "Power Usage (KW)",
               data: usage_data,
               yaxis: 2
              }], {
               xaxes: [{
                 mode: "time",
                 timeformat: "%H:%M",
                 timezone: "browser"
               }],
               yaxes: [{},{position: "right"}]
              });
            };

I suspect there is a simpler way to do this that allows Flot to pick up the data directly from the rendered data object without having to use JSON.stringify array process, but I couldn't find it!

Style.css


Not really much to note here. Just some basic formatting for the few elements I am using. The complete listing is at the end.

Conclusion and next steps


So, that's it for now. As I said at the beginning, this code is certainly not complete! It needs a lot more error checking and testing in failure modes. For instance, what if the network is down or the PowerCost Wifi Monitor is not responding?

Astute observers will also note that the way I have done the "Settings" feature only really works with one browser window at a time. When I have tested changing settings with multiple browsers I have found that it sometimes doesn't pick up the environment variables properly and the refresh rate gets into an "undefined" state and then it just continually loops without any pause - obviously bad!

My next steps will be to learn the Socket.io library in Node.js and then to start connecting the BeagleBone to my DigiX board which I got through Kickstarter a few months ago! Check it out here:



Complete source code

 

app.js

 


This is the complete source code for the app.js file discussed above.
/**
 * Module dependencies.
 */


//Load Dependencies
var express = require('express');
var routes = require('./routes');
var user = require('./routes/user');
var http = require('http');
var path = require('path');
var tungus = require('tungus');
var Engine = require('tingodb');
var mongoose = require('mongoose');
var request = require("request");

//Initialize Mongoose with Tingo
var db = mongoose.connect('tingodb://readingsdb');

//Set up some global variables
var usage;
var temp;
var readings;

//Set environment variables for defaults
process.env['SAMPLES'] = '15';
process.env['REFRESHINT'] = '120';


//Mongoose connects to database
mongoose.connect('tingodb://readingsdb', function (err){
    if (!err) {
        console.log('connected to databse');
    } else {
        throw err;
    }
});

//Set up schema for database
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;

var ReadingsSchema = new Schema({
  time: { type : Date, default: Date.now },
    usage2: Number,
    temp2: Number
    });

var Readings = mongoose.model('Readings', ReadingsSchema);

//Do the simple screen scrape and pop the results into the database.
//Rinse and repeat according to the environment variable for refresh rate
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(usage);
           var n2 = body.search("Sensor Temp");
           temp = body.substr((n2+40),3);
           temp = temp.trim();
           temp = temp - 2;
           console.log(temp);
           var readingInfo = new Readings({
               temp2: temp,
               usage2: usage
            });
        readingInfo.save(function(err, readingInfo){
              if (err) return console.error(err);
            console.dir(readingInfo);
            });
        setTimeout(getData,(process.env.REFRESHINT * 1000));
    }});
}

//run the above function
getData();

//Below are all set by default in Express
var app = express();

// all environments
app.set('port', process.env.PORT || 3008);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

//When the Submit button is pressed, it runs this function and re-renders the page
app.post('/', function(req, res){
    process.env['SAMPLES'] = req.param("samples");
    process.env['REFRESHINT'] = req.param("interval");
    console.log('Samples is set to:  ' + process.env.SAMPLES);
    console.log('Refresh is set to:  ' + process.env.REFRESHINT);    
      Readings.find({}, {}, { sort: { 'time' : -1}, limit: process.env.SAMPLES }, function(err, readings) {
    if (err) return console.error(err);
      res.render('index', 
        { title: 'Power Usage and Temp from PowerCost Monitor',
          refreshRate: process.env.REFRESHINT,
          sampleNum: process.env.SAMPLES,
          readings: readings
              });
           }
          );
       });

//This serves up the page and renders the variables into the Jade template engine.
app.get('/', function(req, res){
  Readings.find({}, {}, { sort: { 'time' : -1}, limit: process.env.SAMPLES }, function(err, readings) {
    if (err) return console.error(err);
      res.render('index', 
        { title: 'Power Usage and Temp from PowerCost Monitor',
          refreshRate: process.env.REFRESHINT,
          sampleNum: process.env.SAMPLES,
          readings: readings
              });
           }
          );
       }
    );

//Start up the server!
http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});


Layout.jade



doctype html
html
  head
      title= title
      link(rel='stylesheet', href='/stylesheets/style.css')
      meta(http-equiv="refresh" content="#{refreshRate}" ) 

  body
  
    block content

Index.jade


extends layout

block content    
    h1= title
    
    h2 Graph over time
    br
    #placeholder(style='width:700px;height:300px')
    
    h2 Table of results
    #tablediv
      table
       thead
         tr 
           td 
             b Timestamp
           td
             b Temperature
           td
             b Power usage
         tbody
             each reading in readings
               tr
                 td #{reading.time}
                 td #{reading.temp2}
                 td #{reading.usage2}

    h2 Settings
    form#dataSamples(name="dataSamples",method="post",action="/")
        label(for "samples") Data samples to graph:
        input#setSamplesInput(type="text", placeholder="samples", name="samples", value="#{sampleNum}")
        br
        label(for "interval") Time between refreshes (seconds):       
        input#setIntervalInput(type="text",placeholder="interval", name="interval", value="#{refreshRate}")
        br
        button#btnSubmit(type="submit") submit
        
    script(src='jquery/dist/cdn/jquery-2.1.0.js')
    script(src='flot/jquery.flot.js')
    script(src='flot/jquery.flot.time.js')
    script.
        window.onload = function(){
           if (window.console)console.log("Executing script");
           var temp_data = [];
           var usage_data = [];
           var reading_data = !{JSON.stringify(readings)};
           for (var i = 0; i < #{sampleNum}; i++)
             temp_data.push([new Date(reading_data[i].time), reading_data[i].temp2]);
           for (var i = 0; i < #{sampleNum}; i++)
             usage_data.push([new Date(reading_data[i].time), reading_data[i].usage2]);
           $.plot($("#placeholder"), [{
               label: "Temperature (C)",
               data: temp_data, 
               },{
               label: "Power Usage (KW)",
               data: usage_data,
               yaxis: 2
              }], {
               xaxes: [{
                 mode: "time",
                 timeformat: "%H:%M",
                 timezone: "browser"
               }],
               yaxes: [{},{position: "right"}]
              });
            };

Style.css


body {
  padding: 50px;
  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}

table
{
border-collapse:collapse;
}
table,th, td
{
border: 1px solid black;
}

td
{
padding:6px;
}