Interfacing IoT with embedded using Node.js, part 5: Web interface
October 03, 2017
We've implemented a web server in Node.js that provides APIs for controlling GPIOs, sampling accelerometers, and reading ADC values. Now we tie it together with a web page that uses these services.
In previous installments, we implemented a web server in Node.js that provides APIs for controlling GPIOs, sampling an accelerometer, and reading ADC values. Now it is time to put it all together with a web page that uses these services to actually control the board.
The top-level container for the user interface is
index.html. It is divided into the following parts:
HEADtag contains references to all the scripts and style sheets for the page. This includes
ts7680.js, which implements the demo functionality and several pages for rendering charts via the Google Charts API
BODYtag contains an onload event handler that calls two functions in
adc_update) to start live updating of LED and ADC values, respectively.
The body of the document contains:
DIVto hold the images displaying the LED states
DIVto hold a message indicating that the image can be clicked on to change LED states
- A button to refresh the LEDs (Normally the LEDs are only refreshed when clicked on. If the LEDs are modified outside the interface this button will refresh the visual appearance to match that of the board)
- A button to sample the accelerometer and display a chart of the samples
DIVto hold a chart containing the ADC values rendered as a gauge for each ADC
DIVto hold the chart containing accelerometer values when they are requested.
Defining global variables and utility functions
First some global variables need to be defined that translate between state names and values, hold LED values, and map LED names (colors) to GPIO files:
Next a couple of utility functions must be defined. The first is a shortcut for getting elements by id:
The second is for performing AJaX requests. It uses the URL to request a
parms parameter (which must be a string to submit for a
POST request) or an empty string for a
GET request, and two callbacks: One if a response is successfully received from the server and one for network failures.
Now there are three functions that deal with showing the state of the TS-7680 I/Os (
ts7680_update()) and one function for controlling the TS-7680 outputs (
Getting accelerometer data from the server
accel() function requests 1000 ms of accelerometer samples from the server and then uses the Google Charts API to display a line chart of the samples for that period.
To retrieve this data from the server, first create a new Google visualization table and add two columns: the X axis (which will show our time) and the total acceleration of each sample, in gs.
Next it's time to declare a couple of local variables:
t0 to hold the time of the first sample (all other sample times will be relative to this), and
scale, which holds the maximum value of a sample.
At this point accelerometer data can be acquired from the server. Call the
AJaX function previously defined, requesting
/accelerometer/1000 (which translates into 1000 ms of samples). Now pass a callback that processes the results (lines of comma separate values) and parses them out into samples containing the time of the sample relative to the first sample (at t=0) and the magnitude of the acceleration (calculated from the individual x, y, and z axis samples).
These samples consist of an array of [ time, value ] pairs, which are suitable for adding to the chart directly:
Now the chart can be created, passing the HTML element created in our index.html file to hold it:
Once the chart is created it is time to render it. Provide the options, which consist of the axis labels:
Charting ADC values
adc_update() function initializes and periodically updates the ADC display.
The first thing to do here is create a table that holds each of the gauges for each ADC value. Each gauge should be labeled with the ADC number:
Next, define the options for the chart, including the size, color appearance, and range of each gauge:
Then create a gauge chart using the HTML element created in index.html for that purpose:
Then the values for each gauge must be set from the actual ADC values on the board. This needs to be done periodically using the
setInterval function, which takes a function to call, as well as the interval at which to call (in this case, every 1000 ms).
In the function that gets called every second, call the
AJaX function to request
/adc. You process the reply by splitting it into an array of ADC values, which then have to be scaled down to the range the gauges are set to. Call the
setValue function from the previously created table to set the value for each gauge, and then call the
chart.draw function to update the chart to reflect the current ADC values:
Updating the LED display
ts7680_update() function will update the LED display to match the current state of the board. This is called during initialization, and any time the user refreshes the state of the LEDs manually by clicking on the Technologic Systems' TS-7680 “Refresh LEDs” button.
ts7680_update() function calls the
AJaX function to request the URL
/gpio/56,5,58,7 on the server. The server will return a JSON array encoding the values of GPIO 56, 5, 58, and 7 in the parameter string
The first thing to do with this string is parse it as JSON and map the reported values (“HIGH”, “LOW”, etc.) into “1” and “0” values. Use the
nval object to map the first GPIO as it is inverted (a normal LED reads LOW when the LED is on, and HIGH when the LED is off). The remaining values are mapped using the
The result of this mapping is an array of 0/1 values corresponding to each LED. Store these values in the global variables B_LED (B for blue), G_LED (G for green), Y_LED (Y for yellow), and R_LED (R for red), to be used in the
toggle_led function. Although this is not strictly necessary (the array could be used directly), it is more descriptive and makes the code more readable:
Now all the values can be joined into a single string (for example, [ “1”, “0”, “1”, “1” ] would become “1011”). This is necessary because a separate image exists for each of the 16 possible LED states, and the id of each image is “led” plus the string encoding the 1/0 states, as shown below:
All 16 images are initially hidden using the CSS style
display:none. The strategy is to only show the image corresponding to the current LED state. However, once an image is shown, the current state must be hidden before showing the next one when the state changes.
The new state can now be shown:
Each of the 16 images listens for mouse clicks and calls the
toggle_led() function when it is the currently visible image. The first thing to do here is see where in the image the mouse was clicked. A local variable also must be declared to hold the new state of the LED:
In the board images provided with the demo, there are four
x0,y0 - x1,y1 rectangular regions, which belong to each of the four LEDs. These regions are:
Given this, first check if the click is in one of these regions, and if not ignore the click:
Otherwise, determine which LED the click belongs to and toggle the variable holding that LED state. Then determine the new value to send to the server and call
AJaX to update the LED’s state:
Next, hide the image showing the previous state of the LEDs:
Finally, display the image showing the new state of the LEDs:
That's a wrap!
That’s it! We hope that you have enjoyed this demonstration of the TS-7680 LEDs, accelerometer, and ADCs using the Node.js package ts7680-demo-server. As you can see, it is easy to install, self-contained, and easy to use.
Stay tuned for more installments in our Node.js series, including how Technologic Systems solved a resource utilization problem using remote sensors, a board, and some logic to send text message notifications to employees when a resource is available.