User avatar
richiepp
Posts: 140
Joined: Wed Dec 19, 2012 4:56 pm

Node.js, serialport, AJAX - Temp Sensor

Sat Jun 11, 2016 1:41 pm

Hey All,
I've started to play around with Node.js and the serialport module to communicate with an Arduino. I've got a temperature sensor on the Arduino that reads and sends a serial signal every second. I'm catching that signal and writing to a text file. I read that text file every second and return the value to a webpage using AJAX. This is fine and good but it seems kinda clunky.

Is it possible to do this without writing to the intermediate text file? Maybe have my node code return a JQuery object and push it the the webpage?

Here is my node code

Code: Select all

'use strict';
var serialport = require('serialport');
var SerialPort = serialport.SerialPort;
var parsers = serialport.parsers;

var port = new SerialPort('/dev/ttyUSB0', {
  baudrate: 115200,
  parser: parsers.readline('\r\n')
});

port.on('open', function() {
  console.log('Port open');
});

port.on('data', function(data) {
  console.log(data);
  fs.writeFile("test.txt", (data));
});

var fs = require('fs');
fs.writeFile("test.txt", "I need some temps!", function(err) {
    if(err) {
        return console.log(err);
    }

    console.log("The file was saved!");
});
Here's the JQuery/AJAX

Code: Select all

  window.onload = function() {
  var ul = document.getElementsByTagName('ul')[0];

  function readTextfile() {
    var xhr = new XMLHttpRequest();

    xhr.onreadystatechange = function() {
      if(xhr.readyState === 4) {
        if(xhr.status == 200) {
          showContents(xhr.responseText, xhr);
        }
      }
    }

    xhr.open('GET', '../node_stuff/test.txt', true);
    xhr.send();
  }

  function showContents(responseText) {
    var li = document.createElement('li');
    li.innerHTML = responseText;
    ul.replaceChild(li, ul.childNodes[0]);
  }

  readTextfile();
  setInterval(readTextfile, 500);

}

Heater
Posts: 15990
Joined: Tue Jul 17, 2012 3:02 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Sun Jun 12, 2016 10:38 pm

richiepp,

The thing to do is forget whatever web server you are using now. We will come back to that later.

Create a web server in your node.js code. It can be very simple only a few lines of code. Like so:

Code: Select all

var express = require('express');
var app = express();

app.get('/', function (req, res) {
    res.send('Hello World!');
});

app.listen(3000, function () {
    console.log('Example app listening on port 3000!');
});
As you might guess when you run that and hit http://localhost:3000 with your browser it will display "Hello world". Or of course use your Pi's IP address if your browser is on a different machine.

Well, we can add to that to send the temperature you have collected from the arduino, perhaps like so, assuming your temperature is in a var called "temperature":

Code: Select all

app.get('/api/temperature', function (req, res) {
    var temp = {
        temperature: temperature,
        timestamp: Date()
    };
    res.send(JSON.stringify(temp));
});
Now from your browser you can hit http://localhost:3000/api/temperature and you should see:

{
temperature: "24.2",
timestamp: "Sun Jun 12 2016 15:21:20 GMT-0700 (Pacific Summer Time)"
}

Now, in your web page, forget about jQuery, and use the HTML5 "fetch" like so:

Code: Select all

fetch('./api/some.json')  
  .then(  
    function(response) {  
      if (response.status !== 200) {  
        console.log('Looks like there was a problem. Status Code: ' +  
          response.status);  
        return;  
      }

      // Examine the text in the response  
      response.json().then(function(data) {  
        console.log(data);  
      });  
    }  
  )  
  .catch(function(err) {  
    console.log('Fetch Error :-S', err);  
  });
Now you have an object in your web pages javascript containing temperature and timstamp that you can do what you like with.


About your web server...

Of course the web page has to come from somewhere so you could keep that and just have the JS it serves up fetch from that API on port 3000.

Personally I would server up the web page and the JS it uses from the node.js program itself. If you look at the express module's documentation and many examples all over the net you will see it's easy to get it to serve up static HTML files and javascript files and whatever you like.

HTML5 fetch:
https://developers.google.com/web/updat ... etch?hl=en

Express:
http://expressjs.com/en/starter/hello-world.html

The magic thing about node.js is that you can have whatever code you have running now, fetching the temperature regularly, and add the web server stuff very easily. It's easy to have a lot of things going on asynchronously in your program like that.

Have fun.

P.S. I don't actually see ant jQuery in your posted code. Never mind there is little use for it now a days. It's better to DOM manipulations with react anyway.

I have a simple example/demo of such a node.js server/client delivering GPIO signals to the browser here:
https://bitbucket.org/zicog/pigpio2html

It uses web sockets to deliver the data, Which is a nice way to send data in "real-time" from server to browser instead of polling with AJAX requests.
Memory in C++ is a leaky abstraction .

User avatar
richiepp
Posts: 140
Joined: Wed Dec 19, 2012 4:56 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Mon Jun 13, 2016 2:04 pm

Wow thanks for such a detailed response Heater!

I started working through your notes and got the server up and running....easy enough. But I'm getting stuck at the second step. So here is the server code with the temperature function, note it doesn't work because "temp" is not defined.

Code: Select all

var express = require('express');
var app = express();

app.get('/', function (req, res) {
    res.send('Hello World!');
});

app.listen(8080, function () {
    console.log('Example app listening on port 8080!');
});
app.get('/api/temperature', function (req, res) {
    var temp = {
        temperature: temperature,
        timestamp: Date()
    };
    res.send(JSON.stringify(temp));
});
The Arduino is spitting out a reading every half second and I'm writing the current temp here

Code: Select all

/var/www/html/node_stuff/current_temp.txt
Do I still need this in the method you're suggesting? If so I'll need to read the file and stick it in a variable called temp.

Thanks for your help with this, and my other questions too!
Rich

Heater
Posts: 15990
Joined: Tue Jul 17, 2012 3:02 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Mon Jun 13, 2016 3:45 pm

richiepp,

I'm not sure what you mean. How can "temp" not be defined? It's right there before it get's used:

Code: Select all

var temp = {...
Do you mean "temperature" ?

No you don't need to save anything to a file first. In fact it's better not to, it will be wearing out your SD card!

From what you describe you already have a node.js program that is reading temperature from the serial port. The plan then is to simply add that web server code to your existing program.

If the serial port code saves the temperature in a variable called "temperature" then the server code will read that and send it to the browser when requested.

Easy ha?
Memory in C++ is a leaky abstraction .

User avatar
richiepp
Posts: 140
Joined: Wed Dec 19, 2012 4:56 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Mon Jun 13, 2016 4:36 pm

Baby steps. Ok, I've tried adding my serialport code to your server example and can get it working halfway. The way it's written now it shows the timestamp but not the contents of the serial stream

Code: Select all

'use strict';
var serialport = require('serialport');
var SerialPort = serialport.SerialPort;
var parsers = serialport.parsers;

var port = new SerialPort('/dev/ttyUSB0', {
  baudrate: 115200,
  parser: parsers.readline('\r\n')
});

port.on('open', function() {
  console.log('Port open');
});

var express = require('express');
var app = express();

app.get('/', function (req, res) {
    res.send('Hello World!');
});

app.listen(8080, function () {
    console.log('Example app listening on port 8080!');
});
app.get('/api/temperature', function (req, res) {
    var temp = {
        temperature: temp,
        timestamp: Date()
    };
    res.send(JSON.stringify(temp));
});

User avatar
richiepp
Posts: 140
Joined: Wed Dec 19, 2012 4:56 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Mon Jun 13, 2016 4:53 pm

ok, here's what might be a dumb question, a fundamental Node.js question I guess. Where do I put the fetch code? Do I create a directory structure like this http://localhost/api/temperature and create and index.html file in the temperature directory. Or are all Node.js pages created on the fly with no static pages whatsoever?

Node noob.

Thanks
Rich

Heater
Posts: 15990
Joined: Tue Jul 17, 2012 3:02 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Mon Jun 13, 2016 6:16 pm

richiepp,

In that code you posted above you have no event handler for data arriving from the port. Something like:

Code: Select all

var temperature;

port.on('data', function(data) {
  console.log(data);
  // Parse data and set the temperature variable here

});
Memory in C++ is a leaky abstraction .

Heater
Posts: 15990
Joined: Tue Jul 17, 2012 3:02 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Mon Jun 13, 2016 6:26 pm

richiepp,

There are many ways to get pages served from node.js.

I guess a simple starting point is just to write an index.html file and save it in a directory called "public" in directory your program is in.

In that index.html file you will put your all your HTML, CSS, and javascript. Just to keep things simple for now. As the project grows you can move the JS and CSS out into their own files.

That JS in index.html will do the fetch from api/temperature at regualar intervals and update whatever you want to display it.

So how to serve that public/index.html from your node server?

Just add the line:

Code: Select all

app.use(express.static('public'));
To your server code.

Now you can access that index.html at:

http://localhost:3000/index.html

Or something like that.

See here for documentation on serving static files from node/express:

http://expressjs.com/en/starter/static-files.html
Memory in C++ is a leaky abstraction .

User avatar
richiepp
Posts: 140
Joined: Wed Dec 19, 2012 4:56 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Mon Jun 13, 2016 8:29 pm

Another baby step. I've got the server set up and it renders the temperature and timestamp as expected. However the data it renders is static. I have to refresh the page to see an updated reading.

Code: Select all

var express = require('express');
var app = express();

app.get('/', function (req, res) {
    res.send('Hello World!');
});

app.listen(8080, function () {
    console.log('Example app listening on port 8080!');
});

'use strict';
var serialport = require('serialport');
var SerialPort = serialport.SerialPort;
var parsers = serialport.parsers;

var port = new SerialPort('/dev/ttyUSB0', {
  baudrate: 115200,
  parser: parsers.readline('\r\n')
});

port.on('open', function() {
  console.log('Port open');
});

var temperature;
   port.on('data', function(data) {
   temperature = (data);
   console.log(data);

});

  app.get('/api/temperature', function (req, res) {
    var temp = {
        temperature: temperature,
        timestamp: Date(),
    };
    res.send(JSON.stringify(temp));
  });

Heater
Posts: 15990
Joined: Tue Jul 17, 2012 3:02 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Tue Jun 14, 2016 4:39 pm

Please read my previous post above. You have not yet created an index.html, which will contain your JS that does the fetch. Use setInterval() to get it to do that every second or so. Add the serve static line to your existing server code.

I don't have time just now to be more specific.
Memory in C++ is a leaky abstraction .

User avatar
richiepp
Posts: 140
Joined: Wed Dec 19, 2012 4:56 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Sun Jun 26, 2016 12:10 am

OK, I'm a few steps closer.
My server and serialport script currently looks like the following. This works when I go to http://localhost:8080/temp and it renders the serial port data.

Code: Select all

var express = require('express');
var app = express();
var temperature;

app.use(express.static('public'));

app.listen(8080, function () {
    console.log('Listening on port 8080!');
});

var serialport = require('serialport');
var SerialPort = serialport.SerialPort;
var parsers = serialport.parsers;

var port = new SerialPort('/dev/ttyUSB0', {
  baudrate: 115200,
  parser: parsers.readline('\r\n')
});

port.on('open', function() {
  console.log('Port open');
});

port.on('data', function(temperature) {
  console.log(temperature);
    app.get('/temp', function (req, res) {
        var temp = {
            temperature,
        //    timestamp: Date()
        };
        res.send(JSON.stringify(temp));
    });
});
Here is my index.html page. The page does render and shows the H1 tag but it doesn't show the serial data. Questions below.

Code: Select all

<html>
  <head>
    <title>Outputting a text file</title>
    <style>
      body{font-size:5em;}
      sup{font-size:.5em;}
    </style>
  </head>
<body>
<h1>Hi There!</h1>
<script>
fetch('./api/some.json')
  .then(
    function(response) {
      if (response.status !== 200) {
        console.log('Looks like there was a problem. Status Code: ' +
          response.status);
        return;
      }

      // Examine the text in the response
      response.json().then(function(data) {
        console.log(data);
      });
    }
  )
  .catch(function(err) {
    console.log('Fetch Error :-S', err);
  });

 setInterval(fetch, 500);
</script>
</body>
</html>
I have two questions here, first is the first line of the script. This is a path right? is "some.json" a generic name or is it looking for a specific file?

Code: Select all

fetch('./api/some.json')
The second question is in relation to the timer. Is this the correct way to use setInterval to check every half a second?

Code: Select all

 setInterval(fetch, 500);
Thanks again for your help in getting this setup!

Heater
Posts: 15990
Joined: Tue Jul 17, 2012 3:02 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Thu Jun 30, 2016 2:35 pm

richiepp,
...first line of the script. This is a path right? is "some.json" a generic name or is it looking for a specific file?
"./api/some.json" is just a place holder I put there to indicate that is where you put the URL of the resource you ate fetching.
It looks just like a path to actual file, and could well be, but a URL need not have any file behind it. The server could get the data for GET requests (fetch) to that URL from a file, or from a data base or in this case from whatever comes in the serial port.

For example the URL could be "/api/temperature". The "api" is just there to indicate this is an "application programming interface"
rather than a regular web page. "temperature" is what you get from there, in JSON format in this case.
Is this the correct way to use setInterval to check every half a second?
You can't call fetch from a setInterval like that. fetch requires the URL as a parameter which setInterval will not provide when it is called. You could do something like this:

setInterval (function () {
fetch('/api/temperature).then(bla bla...
}, 500);

I took your code and tweaked it a bit. Added some styling with bootstrap.css http://getbootstrap.com/ and page updating using react.js https://facebook.github.io/react/

The server code is now this:

Code: Select all

// serial2html
//
// A simple demo of serving serial port data via HTTP GET requests.
// Uses bootstrap.css for styling and react.js for user interface building
//

// Use strict so as to make errors out of some old dangerous JS usage. 
"use strict";

// Use the express "middle ware" to help with creating an HTTP server.
let express = require('express');
let app = express();

// Configuration items
let serverPort = 8888;
let serialPort = '/dev/ttyS0';

// Global record of temperature readings
let temperatureRecord = {
    temperature: 30,
    timestamp:   Date()
};

// Change process name so that it is not just "node" in ps.
process.title = 'serial2html';

// Tell express to serve files from the "public" directory
app.use(express.static('public'));

// Tell express to serve files from the "node_modules" directory
app.use(express.static('node_modules'));

// Start the HTTP server listening for requests
app.listen(serverPort, function () {
    console.log(`Listening on port ${serverPort}`);
});

// Define an API URL from which GET requests will get temperature data
app.get('/api/temperature', function (req, res) {
    // Simply respond with the temperature record
    res.send(temperatureRecord);
});

// Use the serialport module to get temperature datat 
let serialport = require('serialport');
let SerialPort = serialport.SerialPort;
let parsers = serialport.parsers;

// Open the serial port with some configuration
let port = new SerialPort(serialPort, {
    baudrate: 115200,
    parser: parsers.readline('\r\n')
});

// When the port is open just say so
port.on('open', function() {
    console.log('Serial port open');
});

// Check for errors on the serial interface
port.on('error', function(err) {
    console.log(`Serial port error: ${err}`);
});

// Record temperature from serial input and add timestamp
port.on('data', function(data) {
    console.log(`Serial got: ${data}`);
    temperatureRecord.temperature = data;
    temperatureRecord.timestamp = Data();
});
The fetch part of the client looks like this:

Code: Select all

        fetch("/api/temperature").then(function(response) {
            if (response.status !== 200) {
                // Update app state with the error, no data 
                update('Failed to fetch ' + response.status, null);
            } else {
                // Examine the text in the response
                response.json().then(function(data) {
                    // Update app state with the new data, no error 
                    update(null, data);
                }).catch(function(err) {
                    // Update app state with the error, no data 
                    update("Fetch error", null);
                });
            }
        });
    },
I have put the complete client and server code here: https://bitbucket.org/zicog/serial2html

I tried to comment the code as much as possible. Let us know if anything is unclear.
Memory in C++ is a leaky abstraction .

User avatar
richiepp
Posts: 140
Joined: Wed Dec 19, 2012 4:56 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Fri Jul 01, 2016 3:30 pm

It Works!!!! Thanks so much Heater!

Well, it mostly works.... When I pull it up in Safari on my iphone and ipad the page renders but the data does not. I get a status message that says "Fetching Data" and that's it. I tried it on a couple different ports; 8080 and 8888 and get the same result. I even tried setting it to port 80 but the script crashes and doesn't run.

I suspect this is related specifically to apple products blocking somehow. Any ideas?

Thanks again Heater, you've been a tremendous help!
Rich

Heater
Posts: 15990
Joined: Tue Jul 17, 2012 3:02 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Fri Jul 01, 2016 4:11 pm

Hmmm...I don't have an iPhone or iPad to test with here. I'll play with it a bit later today.

If you want to run the server on port 80 you will have to be root.
Memory in C++ is a leaky abstraction .

Heater
Posts: 15990
Joined: Tue Jul 17, 2012 3:02 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Fri Jul 01, 2016 7:45 pm

So, I had the server running on my Linux machine and accessed it from a Win 10 machine over the local LAN. No problem.

I'll get my friend with Mac hardware to try it tomorrow.
Memory in C++ is a leaky abstraction .

Heater
Posts: 15990
Joined: Tue Jul 17, 2012 3:02 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Sat Jul 02, 2016 3:23 am

Looks like the fetch() API is not yet supported in Apple's Safari browser.

https://developer.mozilla.org/en/docs/Web/API/Fetch_API

I just replaced the fetch() with the old XMLHttpRequest in case that is the problem.

Can you do a "git pull" on your copy of the code. Or otherwise get a new version from https://bitbucket.org/zicog/serial2html and try it on Apple again?
Last edited by Heater on Sat Jul 02, 2016 3:49 am, edited 1 time in total.
Memory in C++ is a leaky abstraction .

asandford
Posts: 1998
Joined: Mon Dec 31, 2012 12:54 pm
Location: Waterlooville

Re: Node.js, serialport, AJAX - Temp Sensor

Sat Jul 02, 2016 3:35 am

Have you looked at Node-Red (which is in the latest versions of Raspbian)?

Heater
Posts: 15990
Joined: Tue Jul 17, 2012 3:02 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Sat Jul 02, 2016 4:04 am

node-red is an amazingly slick tool.

I'm not sure how it helps with this problem. Most of the work here is in the browser code (html and js) not the server side.

In my example code:

server.js - 70 lines, a lot of which is comments and blanks.

app.js - In the browser. 117 lines.

A node red flow that does a similar job looks like this:

http://flows.nodered.org/flow/8666510f94ad422e4765

Call me old fashioned but I like to work with actual source code not have a tool that generates this:

Code: Select all

[{"id":"985ecbc7.67a138","type":"websocket-listener","z":"9e538f88.61ac7","path":"/ws/simple","wholemsg":"false"},{"id":"e3e4522e.1c1bb","type":"inject","z":"9e538f88.61ac7","name":"Tick every 5 secs","topic":"test","payload":"","payloadType":"date","repeat":"5","crontab":"","once":false,"x":139,"y":320,"wires":[["45dbf990.ba2408"]]},{"id":"50da04b3.af25fc","type":"websocket out","z":"9e538f88.61ac7","name":"","server":"985ecbc7.67a138","client":"","x":539,"y":320,"wires":[]},{"id":"42a28745.bd5d78","type":"http response","z":"9e538f88.61ac7","name":"","x":510,"y":260,"wires":[]},{"id":"1787be40.e87842","type":"http in","z":"9e538f88.61ac7","name":"","url":"/simple","method":"get","swaggerDoc":"","x":141,"y":260,"wires":[["1857548e.e7a8ab"]]},{"id":"1857548e.e7a8ab","type":"template","z":"9e538f88.61ac7","name":"Simple Web Page","field":"payload","format":"html","template":"<!DOCTYPE HTML>\n<html>\n    <head>\n    <title>Simple Live Display</title>\n    <script type=\"text/javascript\">\n        var ws;\n        var wsUri = \"ws:\";\n        var loc = window.location;\n        console.log(loc);\n        if (loc.protocol === \"https:\") { wsUri = \"wss:\"; }\n        // This needs to point to the web socket in the Node-RED flow\n        // ... in this case it's ws/simple\n        wsUri += \"//\" + loc.host + loc.pathname.replace(\"simple\",\"ws/simple\");\n\n        function wsConnect() {\n            console.log(\"connect\",wsUri);\n            ws = new WebSocket(wsUri);\n            //var line = \"\";    // either uncomment this for a building list of messages\n            ws.onmessage = function(msg) {\n                var line = \"\";  // or uncomment this to overwrite the existing message\n                // parse the incoming message as a JSON object\n                var data = msg.data;\n                //console.log(data);\n                // build the output from the topic and payload parts of the object\n                line += \"<p>\"+data+\"</p>\";\n                // replace the messages div with the new \"line\"\n                document.getElementById('messages').innerHTML = line;\n                //ws.send(JSON.stringify({data:data}));\n            }\n            ws.onopen = function() {\n                // update the status div with the connection status\n                document.getElementById('status').innerHTML = \"connected\";\n                //ws.send(\"Open for data\");\n                console.log(\"connected\");\n            }\n            ws.onclose = function() {\n                // update the status div with the connection status\n                document.getElementById('status').innerHTML = \"not connected\";\n                // in case of lost connection tries to reconnect every 3 secs\n                setTimeout(wsConnect,3000);\n            }\n        }\n        \n        function doit(m) {\n            if (ws) { ws.send(m); }\n        }\n    </script>\n    </head>\n    <body onload=\"wsConnect();\" onunload=\"ws.disconnect();\">\n        <font face=\"Arial\">\n        <h1>Simple Live Display</h1>\n        <div id=\"messages\"></div>\n        <button type=\"button\" onclick='doit(\"click\");'>Click to send message</button>\n        <hr/>\n        <div id=\"status\">unknown</div>\n        </font>\n    </body>\n</html>\n","x":338,"y":260,"wires":[["42a28745.bd5d78"]]},{"id":"45dbf990.ba2408","type":"function","z":"9e538f88.61ac7","name":"format time nicely","func":"msg.payload = Date(msg.payload).toString();\nreturn msg;","outputs":1,"noerr":0,"x":327,"y":320,"wires":[["50da04b3.af25fc"]]},{"id":"eccc8bc2.133378","type":"websocket in","z":"9e538f88.61ac7","name":"","server":"985ecbc7.67a138","client":"","x":326,"y":372,"wires":[["9adfff59.652"]]},{"id":"9adfff59.652","type":"debug","z":"9e538f88.61ac7","name":"","active":true,"console":"false","complete":"false","x":516,"y":372,"wires":[]}]
Memory in C++ is a leaky abstraction .

User avatar
richiepp
Posts: 140
Joined: Wed Dec 19, 2012 4:56 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Sat Jul 02, 2016 5:22 pm

Awesome it works in Safari now!

Now that the data is flowing I can start playing with the layout and user interface. After trying to tweak the app.js aka the React script I'm finding it's a little more involved than just tweaking the html. I'll be reading up on react.js in the coming days.

I'd like to be able to edit the timestamp format and maybe stick a temperature reading in the <title> element of the page so it renders in the browser tab.

My next steps are to add more temperature sensors into the mix. I'm not sure how many more but in the ballpark of a dozen or so. I can have the Arduino send the readings as a string; maybe comma separated? Then split up the sting and render them independently. I tried this by changing the way the serial buffer is parsed in the server.js script

Code: Select all

//    parser: parsers.readline('\r\n')
    parser: parsers.raw
This works as intended when you look at from the console but the react script, app.js, doesn't seem to like it and just renders "--".

How can I read the string and render it?

Thanks again Heater!

Heater
Posts: 15990
Joined: Tue Jul 17, 2012 3:02 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Sat Jul 02, 2016 6:19 pm

richiepp,

Excellent!

Yes, react is a bit more involved that "tweaking" some HTML. It's an overkill for displaying a single temperature.

At the end of the day in a "web app", which is basicaly what you have here, you end up with some HTML and then
you need some JS to add, remove and update HTML elements. There are many ways to do this including the famous old jQuery
library and today a few dozen "frameworks". Or just use the undelying browser API (Yuk).

I like react.js. It's a flexible, fast, and pretty simple way of generating HTML components and updating them with new data.
(In fact there is no HTML, that JSX syntax in the react components code is not HTML. It is transformed into Javascript
that updates your page by going straight to the browser API)

So here is what I'm thinking:

1) The arduino can send many temperatures per line. Why not have the Arduino send them in JSON format already? That makes
parsing the data trivial in our server (JSON.parse(line)). Like the following, but have it all on one line so that your serial line parser can separtate them all out as they arrive:

Code: Select all

[
    {
        location:    lounge
        temperature: 24
        status:      "online"
    },
    {
        location:    kichen
        temperature: 26
        status:      "online"
    },
    {
        location:    bathroom
        temperature: null
        status:      "offline"
    }
]
Or just send a simple array or temp values.

2) This JS object can then find it's way to the browser though AJAX in the same way it does now.

3) Split each row of the displayed table into a separtate react component. Have react render as many of these rows as there are.

Let me think on this a bit...
Memory in C++ is a leaky abstraction .

User avatar
richiepp
Posts: 140
Joined: Wed Dec 19, 2012 4:56 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Sat Jul 02, 2016 6:59 pm

Let me say more about my project.

So this is a "home automation" project that will include a variety of sensors and switches. Each room in the house will have it's own web page that will render temperature and any other sensor data from that room. It'll also include a keypad to toggle lights and render their current status; on, off or dimmed. I have a template of what this looks like but I don't want to post it publicly, I'll send you a PM.

I'll also want to be logging temperature data, it seems like Plot.ly and ThingSpeak are popular services these days. ThingSpeak has a node module so that might be the way to go.

Kinda thinking out loud here....

Heater
Posts: 15990
Joined: Tue Jul 17, 2012 3:02 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Sun Jul 03, 2016 1:26 am

That's a cool project you have going on there.

A nice thing abut react is that you can add react components to any existing HTML you have. Just put a div in there and tell react to render a component into it.

plot.ly looks interesting. Thanks for the heads up on that.
Memory in C++ is a leaky abstraction .

Squirrelbd
Posts: 1
Joined: Wed Oct 16, 2019 7:42 am

Re: Node.js, serialport, AJAX - Temp Sensor

Wed Oct 16, 2019 7:50 am

Heater wrote:
Sat Jul 02, 2016 4:04 am
node-red is an amazingly slick tool.

I'm not sure how it helps with this problem. Most of the work here is in the browser code (html and js) not the server side.

In my example code:

server.js - 70 lines, a lot of which is comments and blanks.

app.js - In the browser. 117 lines.

A node red flow that does a similar job looks like this:

http://flows.nodered.org/flow/8666510f94ad422e4765

Call me old fashioned but I like to work with actual source code not have a tool that generates this:

Code: Select all

[{"id":"985ecbc7.67a138","type":"websocket-listener","z":"9e538f88.61ac7","path":"/ws/simple","wholemsg":"false"},{"id":"e3e4522e.1c1bb","type":"inject","z":"9e538f88.61ac7","name":"Tick every 5 secs","topic":"test","payload":"","payloadType":"date","repeat":"5","crontab":"","once":false,"x":139,"y":320,"wires":[["45dbf990.ba2408"]]},{"id":"50da04b3.af25fc","type":"websocket out","z":"9e538f88.61ac7","name":"","server":"985ecbc7.67a138","client":"","x":539,"y":320,"wires":[]},{"id":"42a28745.bd5d78","type":"http response","z":"9e538f88.61ac7","name":"","x":510,"y":260,"wires":[]},{"id":"1787be40.e87842","type":"http in","z":"9e538f88.61ac7","name":"","url":"/simple","method":"get","swaggerDoc":"","x":141,"y":260,"wires":[["1857548e.e7a8ab"]]},{"id":"1857548e.e7a8ab","type":"template","z":"9e538f88.61ac7","name":"Simple Web Page","field":"payload","format":"html","template":"<!DOCTYPE HTML>\n<html>\n    <head>\n    <title>Simple Live Display</title>\n    <script type=\"text/javascript\">\n        var ws;\n        var wsUri = \"ws:\";\n        var loc = window.location;\n        console.log(loc);\n        if (loc.protocol === \"https:\") { wsUri = \"wss:\"; }\n        // This needs to point to the web socket in the Node-RED flow\n        // ... in this case it's ws/simple\n        wsUri += \"//\" + loc.host + loc.pathname.replace(\"simple\",\"ws/simple\");\n\n        function wsConnect() {\n            console.log(\"connect\",wsUri);\n            ws = new WebSocket(wsUri);\n            //var line = \"\";    // either uncomment this for a building list of messages\n            ws.onmessage = function(msg) {\n                var line = \"\";  // or uncomment this to overwrite the existing message\n                // parse the incoming message as a JSON object\n                var data = msg.data;\n                //console.log(data);\n                // build the output from the topic and payload parts of the object\n                line += \"<p>\"+data+\"</p>\";\n                // replace the messages div with the new \"line\"\n                document.getElementById('messages').innerHTML = line;\n                //ws.send(JSON.stringify({data:data}));\n            }\n            ws.onopen = function() {\n                // update the status div with the connection status\n                document.getElementById('status').innerHTML = \"connected\";\n                //ws.send(\"Open for data\");\n                console.log(\"connected\");\n            }\n            ws.onclose = function() {\n                // update the status div with the connection status\n                document.getElementById('status').innerHTML = \"not connected\";\n                // in case of lost connection tries to reconnect every 3 secs\n                setTimeout(wsConnect,3000);\n            }\n        }\n        \n        function doit(m) {\n            if (ws) { ws.send(m); }\n        }\n    </script>\n    </head>\n    <body onload=\"wsConnect();\" onunload=\"ws.disconnect();\">\n        <font face=\"Arial\">\n        <h1>Simple Live Display</h1>\n        <div id=\"messages\"></div>\n        <button type=\"button\" onclick='doit(\"click\");'>Click to send message</button>\n        <hr/>\n        <div id=\"status\">unknown</div>\n        </font>\n    </body>\n</html>\n","x":338,"y":260,"wires":[["42a28745.bd5d78"]]},{"id":"45dbf990.ba2408","type":"function","z":"9e538f88.61ac7","name":"format time nicely","func":"msg.payload = Date(msg.payload).toString();\nreturn msg;","outputs":1,"noerr":0,"x":327,"y":320,"wires":[["50da04b3.af25fc"]]},{"id":"eccc8bc2.133378","type":"websocket in","z":"9e538f88.61ac7","name":"","server":"985ecbc7.67a138","client":"","x":326,"y":372,"wires":[["9adfff59.652"]]},{"id":"9adfff59.652","type":"debug","z":"9e538f88.61ac7","name":"","active":true,"console":"false","complete":"false","x":516,"y":372,"wires":[]}]
Can we pass multiple entry from node-red to website using single websocket? How can we do it? Any example?

Heater
Posts: 15990
Joined: Tue Jul 17, 2012 3:02 pm

Re: Node.js, serialport, AJAX - Temp Sensor

Sat Oct 19, 2019 10:24 am

Sorry I have no idea. I do not use node-red.

You can send and receive whatever messages you like over a web socket.

I like to use socket.io to make that easy: https://socket.io/
Memory in C++ is a leaky abstraction .

Return to “Other programming languages”