Node.js how to work with Serial ports and display data on a beautiful frontend using Highcharts API Part 2





Now you have some basic idea of the technologies we are using here. So let's get our hands dirty.

First, we have to set up the Node js libraries correctly to handle the serial data and open a WebSocket.

So simple what we here going to do is getting data from USB and put them into a GUI to visualize our data nicely.

So for this tutorial, I'm going to use Arduino as my data source. Piratically this will be your sensor or devices which you are using.

Here http://www.highcharts.com/ 
Highcharts makes it easy for developers to set up interactive charts in their web pages. This has good community support and this is best for our works.

1. First step

clone this repository

https://github.com/akilawickey/nodejs-serial-frontend/tree/master

Here is the Arduino sketch sample.ino which I'm using to emulate data which you can find inside the repository


1:   /*  
2:   * This code will emulate USB data  
3:   *  
4:   *  
5:   */  
6:  // the setup function runs once when you press reset or power the board  
7:  void setup() {  
8:   // initialize digital pin 13 as an output.  
9:   Serial.begin(9600);  
10:  }  
11:  // the loop function runs over and over again forever  
12:  void loop() {  
13:    Serial.println("#M,5,!");  
14:    delay(1000);   
15:    Serial.println("#T,1,19,29,30,29,26,26,26,29,26,26,30,29,27,26,22,0,26,0,!");  
16:    delay(1000);   
17:    Serial.println("#M,12,!");  
18:    delay(1000);   
19:    Serial.println("#T,1,29,29,22,29,22,23,26,29,26,26,30,29,26,26,29,0,26,0,!");  
20:    delay(1000);   
21:    Serial.println("#M,15,!");  
22:    delay(1000);   
23:    Serial.println("#T,1,22,29,30,29,26,26,26,29,26,26,30,22,26,26,29,0,26,0,!");  
24:    delay(1000);  
25:    Serial.println("#M,17,!");  
26:    delay(1000);   
27:    Serial.println("#T,1,25,29,30,29,25,26,26,29,22,26,30,29,26,26,22,0,26,0,!");  
28:    delay(1000);   
29:    Serial.println("#M,20,!");  
30:    delay(1000);   
31:  }  


here I created two types of data. From M I'm specifying a gauge in the front end think this is a speed of a car.
From T as an example, I'm going to display a Graph of a set of values think this is some temperature values of the battery cell.

2. Next step

Upload this code to the Arduino and you can have a virtual stream of serial data.

Now you have to run the node js backend to get this serial data and put them to a WebSocket which can push data to the GUI.

3. Next step

Now let's move to the Node js backend part

So you have cloned the repository where you have to run the test.js file.

To run it you have to install node modules for serialport,socket.io, express

you can grab it by searching nodejs tutorials on how to install node modules to run an app.

After installing required packages in the terminal type

node test.js

then the node server will start.

and serial data will print on the terminal.


3. final step

And here is the cool step go to the Google Chrome and http://localhost:3000/ 
and you can open up the GUI here you can see graph is changing according to data.




Understanding the source codes

here is the test.js code

here this will create a simple HTTP server and send USB serial data to the index.html using sockets.

Here express is a framework which is used to rendering HTML pages. HTTP will create a basic HTTP server. Here router directing the path to the index.html file.

serial port is Node.js package to access serial ports for reading and writing

socket.io is will enables real-time, bi-directional communication between web clients and servers. It has two parts: a client-side library that runs in the browser, and a server-side library for Node.js. Here we used it to push serial port data to the web socket


1:  /*  
2:  NODE js library handler to get data from serial ports and push them to the websockets and display in GUI  
3:  */  
4:  var express = require("express");  
5:  var app = express();  
6:  var http = require('http').Server(app);  
7:  var path = __dirname + '/';  
8:  var io = require('socket.io')(http);  
9:  var router = express.Router();  
10:  var SerialPort = require('serialport'); /*Serial Port Intitiate*/  
11:  var port = new SerialPort("/dev/ttyUSB0", {  
12:   baudrate: 9600,  
13:   bufferSize: 1 ,  
14:   rtscts: true ,  
15:  });  
16:  var str = "";  
17:  var count=0;  
18:  var no_pkt = 0;  
19:  var flag_V = 0; /*Validation Flag*/  
20:  /*Socket IO*/  
21:  router.use("/",function(req,res){  
22:   res.sendFile(path + "index.html");  
23:  });  
24:  app.use("/",router);  
25:  app.use(express.static(__dirname + '/public'));  
26:  port.on('data', function (data) {  
27:   if(flag_V == 0) validateData(data) ;  
28:   else{  
29:        str += data;  
30:        if(data == "!"){        
31:             myPrint(str);  
32:             count = 0;  
33:             io.emit('chat message', str);     //send msg to web interface.  
34:             str=""  
35:             flag_V = 0;  
36:             no_pkt++;  
37:             console.log("data number :" + no_pkt);  
38:        }        
39:        count++;  
40:   }  
41:  });  
42:  io.on('connection', function(socket){  
43:    console.log('User connected'); // this will print when users are connected  
44:    socket.on('chat message', function(msg){  
45:    });  
46:    socket.on('disconnect', function(data) {  
47:      console.log('-----------------disconnected the socket!-------------');  
48:    });  
49:  });  
50:  /*Create http server*/  
51:  app.get('/', function(req, res){  
52:   res.sendFile(__dirname + '/index.html');  
53:  });  
54:  http.listen(3000, function(){  
55:   console.log('listening on :3000');  
56:   console.log('--------------------Server Started---------------------------');  
57:  });  
58:  // these functions are for data validation  
59:  function myPrint(data) {  
60:       var i;  
61:        console.log('Data: ' + data);  
62:  }  
63:  // this function will validate data  
64:  function validateData(x){  
65:       if(x != "#"){  
66:            port.flush();  
67:       }else if( x == "#"){  
68:            flag_V=1;  
69:            console.log("Validated");  
70:       }  
71:  }  



And here is the index.html where we are GUI stuff is working. I think you can get an idea after looking at this code. You can debug inside the browser using console.log commands to check whether data is coming or not. Here we have to import some libraries from high charts and



1:  <!--   
2:                Backend GUI  
3:   -->  
4:  <!doctype html>  
5:  <html>  
6:   <head>  
7:    <title>Serial Data Plot </title>  
8:    <!-- Websocket IO -->  
9:    <script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>  
10:    <script src="http://code.jquery.com/jquery-1.11.1.js"></script>  
11:    <!-- ANUGULAR SPEEDOMETER AND HOME COMPONENTS-->  
12:    <!-- Battry_Chart Imports -->  
13:    <script src="https://code.highcharts.com/highcharts.js"></script>  
14:    <script src="https://code.highcharts.com/modules/exporting.js"></script>  
15:    <script src="https://code.highcharts.com/modules/solid-gauge.js"></script>  
16:    <script src="http://code.highcharts.com/stock/highstock.src.js"></script>  
17:    <script src="https://code.highcharts.com/modules/solid-gauge.js"></script>  
18:    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>  
19:    <script src="https://code.highcharts.com/highcharts-more.js"></script>  
20:    <script src="https://code.highcharts.com/modules/solid-gauge.js"></script>  
21:      <script type="text/javascript" src="battryChart.js"></script>  
22:  <!-- JavaScript -->  
23:    <!-- <script src="//cdn.jsdelivr.net/alertifyjs/1.8.0/alertify.min.js"></script> -->  
24:    <!-- Tabs -->  
25:    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">  
26:    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>  
27:    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>  
28:    <style>  
29:     body{   
30:     padding-top:1px;  
31:     background-image:url("../public/back.jpg");  
32:     }  
33:     .navbar{  
34:      min-height:20px;   
35:      /*or whatever height you require*/  
36:     }  
37:     .six-sec-ease-in-out {  
38:     -webkit-transition: width 6s ease-in-out;  
39:     -moz-transition: width 6s ease-in-out;  
40:     -ms-transition: width 6s ease-in-out;  
41:     -o-transition: width 6s ease-in-out;  
42:     transition: width 6s ease-in-out;  
43:     }  
44:     .container { margin-top: 50px; }  
45:     .progress-bar-vertical {  
46:      width: 50px;  
47:      min-height: 500px;  
48:      display: flex;  
49:      align-items: flex-end;  
50:      margin-right: 20px;  
51:      float: left;  
52:     }  
53:     .progress-bar-vertical .progress-bar {  
54:      width: 100%;  
55:      height: 0;  
56:      -webkit-transition: height 0.6s ease;  
57:      -o-transition: height 0.6s ease;  
58:      transition: height 0.6s ease;  
59:     }  
60:     .label {  
61:      font-size: 23px;  
62:      font-style: italic;  
63:     }  
64:     .row {margin-top: 35px;}  
65:     .nopadding {  
66:     padding: 0;  
67:     margin: 0;  
68:     }  
69:     .tablecontainer { width: 1200px; overflow; hidden;}  
70:     tr {display: block; }  
71:     th, td { width: 1200px; }  
72:     tbody { display: block; height: 300px; overflow: auto;}  
73:    </style>  
74:    <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />  
75:    <style type="text/css">  
76:     html { height: 100%; }  
77:     body { height: 100%;}  
78:     #map_canvas {   
79:      position: absolute;  
80:      height: 60%;  
81:      width: 40%;   
82:      float:right;  
83:      top: 190px;  
84:      left:700px;  
85:     }  
86:     /*border: 3px solid #73AD21;*/  
87:     #temp{  
88:     position: absolute;  
89:     z-index: 999;   
90:     }  
91:    </style>  
92:   </head>  
93:   <body onload="javascript:Init()" ng-controller="demoController as dm" style="background-color:#262626;color:white;">  
94:   <!-- Tabs -->  
95:   <nav class="navbar navbar-default" style="font-family: Lucida Sans Unicode, Lucida Grande, sans-serif;">  
96:    <div class="container-fluid">  
97:    <div class="navbar-header">  
98:      <a class="navbar-brand" href="index.html">Serial Data Plot</a>  
99:     </div>  
100:     <ul class="nav navbar-nav">  
101:      <li class="active"><a href="javascript:void(0)" class="tablinks" onclick="openCity(event, 'home')">Home</a></li>  
102:      <li><a href="javascript:void(0)" class="tablinks" onclick="openCity(event, 'battery')">data1</a></li>  
103:      <li><a href="javascript:void(0)" class="tablinks" onclick="openCity(event, 'data')">data2</a></li>  
104:      <li><a href="javascript:void(0)" class="tablinks" onclick="openCity(event, 'Debug')">data3</a></li>  
105:     </ul>  
106:    </div>  
107:   </nav>  
108:     </div>  
109:    <!-- Tab Paris -->  
110:   <div id="battery" class="tabcontent">  
111:      <!-- add data here -->  
112:   </div>  
113:   <div id="data" class="tabcontent">  
114:          <!-- add data here -->  
115:   </div>  
116:    </div>  
117:    <div id="home" class="tabcontent">  
118:       <div class="alert alert-success" style="width: 30%;">  
119:         <span>  
120:         <a href="#" id = "msg" class="close" data-dismiss="alert" aria-label="close">&times;</a>  
121:         <strong>Success!</strong> Data Recieved.  
122:         </span>  
123:       </div>    
124:       <div class= "col-md-5 nopadding">  
125:         <div id="temp" style="width: 97%;"></div>  
126:       </div>  
127:       <div>  
128:         <highchart id="container-torque" config="chartConfig" style="width:250px;height:220px;float:left">  
129:       </div>  
130:    </div>  
131:    <div id="Debug" class="tabcontent">  
132:      <div id="sTitle" style="margin: 20px;  
133:                  font-size: 20px;  
134:                  font-family: Lucida Sans Unicode, Lucida Grande, sans-serif;" > Data Received   
135:      </div>  
136:      <div id="test" style="margin-left: 80px;   
137:                 border: 2px solid black;  
138:                 border-radius: 10px;   
139:                 width:1150px;  
140:                 height: 200px;  
141:                 overflow: scroll;  
142:                 font-size: 18px;  
143:                 padding-left: 10px;  
144:                 border-width: 1px;">  
145:      </div>  
146:    </div>  
147:    <script type="text/javascript">  
148:             function getDateTime() {  
149:                var now   = new Date();   
150:                var year  = now.getFullYear();  
151:                var month  = now.getMonth()+1;   
152:                var day   = now.getDate();  
153:                var hour  = now.getHours();  
154:                var minute = now.getMinutes();  
155:                var second = now.getSeconds();   
156:                if(month.toString().length == 1) {  
157:                  var month = '0'+month;  
158:                }  
159:                if(day.toString().length == 1) {  
160:                  var day = '0'+day;  
161:                }    
162:                if(hour.toString().length == 1) {  
163:                  var hour = '0'+hour;  
164:                }  
165:                if(minute.toString().length == 1) {  
166:                  var minute = '0'+minute;  
167:                }  
168:                if(second.toString().length == 1) {  
169:                  var second = '0'+second;  
170:                }    
171:                var dateTime = hour+':'+minute+':'+second;    
172:                 return dateTime;  
173:              }  
174:    </script>  
175:    <script>  
176:    var chart;  
177:    var batt_temp;  
178:    var y = [];  
179:    var speed;  
180:    var socket = io();  
181:     socket.on('chat message', function(msg){  
182:      var res = msg.split(",");  
183:      time = getDateTime();  
184:      console.log(res);  
185:     if(res[0] == 'T'){  
186:       batt_temp = [];  
187:       y[0] = [];  
188:        for(var i=1; i<19; i++) {   
189:         res[i] = parseInt(res[i], 10);       
190:          if(res[i]>100){  
191:           res[i] = 0;  
192:          }  
193:          batt_temp.push(res[i]);  
194:        }   
195:          y[0] = batt_temp;   
196:          //console.log(batt_temp)  
197:     }else if(res[0] == 'M'){   
198:          speed = res[1];         
199:       }  
200:         $("#msg").empty();   
201:       $("#msg").text(res[0]);   
202:     });  
203:   //This is not a highcharts object. It just looks a little like one!  
204:  var chartConfig = {  
205:   options: {  
206:     //This is the Main Highcharts chart config. Any Highchart options are valid here.  
207:     //will be overriden by values specified below.  
208:     chart: {  
209:       type: 'bar'  
210:     },  
211:     tooltip: {  
212:       style: {  
213:         padding: 10,  
214:         fontWeight: 'bold'  
215:       }  
216:     }  
217:   },  
218:   //The below properties are watched separately for changes.  
219:   //Series object (optional) - a list of series using normal Highcharts series options.  
220:   series: [{  
221:     data: [10, 15, 12, 8, 7]  
222:   }],  
223:   //Title configuration (optional)  
224:   title: {  
225:     text: 'Hello'  
226:   },  
227:   //Boolean to control showing loading status on chart (optional)  
228:   //Could be a string if you want to show specific loading text.  
229:   loading: false,  
230:   //Configuration for the xAxis (optional). Currently only one x axis can be dynamically controlled.  
231:   //properties currentMin and currentMax provided 2-way binding to the chart's maximum and minimum  
232:   xAxis: {  
233:   currentMin: 0,  
234:   currentMax: 20,  
235:   title: {text: 'values'}  
236:   },  
237:   //Whether to use Highstocks instead of Highcharts (optional). Defaults to false.  
238:   useHighStocks: false,  
239:   //size (optional) if left out the chart will default to size of the div or something sensible.  
240:   size: {  
241:    width: 400,  
242:    height: 300  
243:   },  
244:   //function (optional)  
245:   func: function (chart) {  
246:    //setup some logic for the chart  
247:   }  
248:  };  
249:  //-------------------------------Battery---------------------------------------------------  
250:      chat_battery = Highcharts.chart('temp', {  
251:         chart: {  
252:           type: 'line'  
253:         },  
254:         title: {  
255:           text: 'Cell Temperatures'  
256:         },  
257:         subtitle: {  
258:           text: 'Source: VEGA'  
259:         },  
260:         yAxis: {  
261:           title: {  
262:             text: 'Temperature (°C)'  
263:           }  
264:         },  
265:         plotOptions: {  
266:           line: {  
267:             dataLabels: {  
268:               enabled: true  
269:             },  
270:             enableMouseTracking: true  
271:           }  
272:         },  
273:         series: [{  
274:           name: 'Battery cell Temperatures',  
275:           data: Math.abs(batt_temp) % 100  
276:         }]  
277:       });   
278:  //------------------Guage-----------------------------------------------------------  
279:  $(function () {  
280:    var gaugeOptions = {  
281:      chart: {  
282:        type: 'solidgauge',  
283:        backgroundColor: '#F6F6F6'  
284:      },  
285:      title: null,  
286:      pane: {  
287:        center: ['50%', '60%'],   
288:        borderWidth: 0,  
289:        size: '80%',  
290:        startAngle: -90,  
291:        endAngle: 90,   
292:        background: {  
293:          backgroundColor: (Highcharts.theme && Highcharts.theme.background2) || '#FFF',  
294:          innerRadius: '60%',  
295:          outerRadius: '100%',  
296:          shape: 'arc'  
297:        }  
298:      },  
299:      tooltip: {  
300:        enabled: false  
301:      },  
302:      // the value axis  
303:      yAxis: {  
304:        stops: [  
305:          [0.1, '#55BF3B'], // green  
306:          [0.5, '#DDDF0D'], // yellow  
307:          [0.9, '#DF5353'] // red  
308:        ],  
309:        lineWidth: 0,  
310:        minorTickInterval: null,  
311:        tickAmount: 2,  
312:        title: {  
313:          y: -70  
314:        },  
315:        labels: {  
316:          y: 16  
317:        }  
318:      },  
319:      plotOptions: {  
320:        solidgauge: {  
321:          animation: {  
322:            duration :1,  
323:            easing: 'easeOutBounce'  
324:          },  
325:          dataLabels: {  
326:            y: 5,  
327:            borderWidth: 0,  
328:            useHTML: true  
329:          }  
330:        }  
331:      }  
332:    };  
333:    var chartlTorque = Highcharts.chart('container-torque', Highcharts.merge(gaugeOptions, {  
334:      yAxis: {  
335:        min: 0,  
336:        max: 20,  
337:        title: {  
338:          text: 'speed'  
339:        }  
340:      },  
341:      series: [{  
342:        name: 'Speed',  
343:        data: [1],  
344:        dataLabels: {  
345:          format: '<div style="text-align:center"><span style="font-size:25px;color:' +  
346:            ((Highcharts.theme && Highcharts.theme.contrastTextColor) || 'black') + '">{y:.1f}</span><br/>' +  
347:              '<span style="font-size:12px;color:silver">* 1000 / min</span></div>'  
348:        },  
349:        tooltip: {  
350:          valueSuffix: ' revolutions/min'  
351:        }  
352:      }]  
353:    }));  
354:    //---------------------------------------------------------------------------------------------------  
355:  });  
356:  function reload(){  
357:   chat_battery.series[0].setData(batt_temp);  
358:  }  
359:  setInterval(reload, 1000);  
360:  function openCity(evt, cityName) {  
361:    var i, tabcontent, tablinks;  
362:    tabcontent = document.getElementsByClassName("tabcontent");  
363:    for (i = 0; i < tabcontent.length; i++) {  
364:      tabcontent[i].style.display = "none";  
365:    }  
366:    tablinks = document.getElementsByClassName("tablinks");  
367:    for (i = 0; i < tablinks.length; i++) {  
368:      tablinks[i].className = tablinks[i].className.replace(" active", "");  
369:    }  
370:    document.getElementById(cityName).style.display = "block";  
371:    evt.currentTarget.className += " active";  
372:  }  
373:  function Init(){  
374:    openCity(event, 'home');  
375:    // initialize();   
376:  }  
377:  </script>  
378:  </body>  
379:  </html>  


If you have any problems regarding this you can contact me.

Thank you

Comments

Post a Comment

Comment

Popular posts from this blog

IOT Greenhouse project part 1

How to fix FirebaseApp with name [DEFAULT] doesn't exist issue with flutter