Using NodeJS to develop a temperature server with the Raspberry Pi
The Raspbian Linux distribution for the Raspberry Pi includes some useful kernel drivers for accessing devices connected to the Pi’s GPIO pins. Based on the tutorial from the University of Cambridge Computer Laboratory I’ve been playing around with the DS18B20 digital thermometer on the Pi. I’ve connected the sensor to the Pi using a standard electronics breadboard and the excellent Adafruit Pi Cobbler breakout connector kit (kudos to Mills for her excellent soldering skills). When the required GPIO kernel modules are loaded a file containing sensor output is written to the /sys/bus directory (see tutorial link above for more) which contains the current thermometer reading.
Developing with NodeJS
I originally wrote a Python CGI script as part of the CPC Pi Hack event to parse the sensor file and display the temperature on a web page (although we didn’t submit our hack in the end), but I’ve also been looking for a project for a while to try out NodeJS.The result is a prototype JavaScript server/client app to serve temperature from the Pi as a JSON string and display a graph of current temperature on the client.
This was my first NodeJS app and I was impressed with the speed of development and the readability of documentation/examples – so much so that I managed to write the bulk of the server on my netbook during a flight from Leeds to London! The biggest disadvantage of using NodeJS on the Pi is the time it takes to compile (1hr 58 minutes, Pi CPU clocked at 950MHz). While this may put some developers off, the build process was painless and is more than made up for by the efficient asynchronous nature of NodeJS once you get it running (first-pass testing shows no noticeable increase in memory/CPU when the server is under load although I’ve not investigated this thoroughly).
The server code is divided into two parts: a dynamic server response which is called when a request for the “temperature.json” URL is received. The server reads the sensor file, parses the data and returns the temperature with a Unix time-stamp in JSON notation. Here’s a snippet from the server code parsing and returning the temperature data:
// Read data from file (using fast node ASCII encoding). var data = buffer.toString('ascii').split(" "); // Split by space // Extract temperature from string and divide by 1000 to give celsius var temp = parseFloat(data[data.length-1].split("=")[1])/1000.0; // Round to one decimal place temp = Math.round(temp * 10) / 10 // Add date/time to temperature var jsonData = [Date.now(), temp]; // Return JSON data response.writeHead(200, { "Content-type": "application/json" }); response.end(JSON.stringify(jsonData), "ascii");
The second section of the server uses the node-static module to serve a client-side page which performs an AJAX call for “temperature.json” and plots current temperature. The plot is created using the highcharts JavaScript package to create a dynamic graph which moves along the x-axis over time (check out the highcharts demo page to get a better idea of dynamic charts). One thing to note is that the sensor precision is ±0.5 °C, and while the temperature data is rounded to one decimal place, the default highcharts y-axis precision may be a bit misleading. Overall though the highcharts package is pretty slick, and the plot looks great on my new Nexus 7!
I’ve pushed the code to GitHub as it may be useful to others who are also new to the Pi and NodeJS. One of the features of NodeJS I like the most is the ease of testing and deployment – you can run the server right in the terminal window without super-user permissions and get debugging info straight away. Furthermore, given the ease of creating web-apps and the small resource footprint (obviously depending on what you’re doing) I’ll definitely be looking to use NodeJS/JavaScript as development platform for the Pi in the future.
After some weird problems with node-static (I’m still not sure its installed properly but it doesn’t complain any more) I’m getting the error: “Error serving /favicon.ico – Not Found”
Any ideas?
Hi Matthew, the favicon errors you’re seeing is a browser based request for a website icon, and you may see requests for other stuff depending on which browser you use. I see the same, but it’s nothing to worry about except for the annoying error messages. Options to fix are to create a website icon or add a redirect in the code. Check out this discussion for more info: http://stackoverflow.com/questions/1321878/how-to-prevent-favicon-ico-requests. I may push a fix to github if I get time.
Great to hear you’ve got it working though!
Hi Tom, thanks for the info. This is going to sound stupid but I completely misunderstood and thought that just by going to the url (without the temperature_plot.htm) would load the page and I was getting stuck at a 404 file not found error; this is the first node example I’ve tried to get running though. It wasn’t until earlier I realised I had to go to the htm page;now I’m seeing the chart and all which is cool.
Cheers
Hi Matthew,
Just as an update I’ve pushed some changes to github which catch favicon requests, and also make the server provide proper JSON data.
Updated files: https://github.com/talltom/PiThermServer/commit/c1bce618dc2451762d19c7d2355194415a6005ed
Cheers.
I have a Heatmiser Wifi Thermostat at home and this little script is rather cool to have:
https://code.google.com/p/heatmiser-wifi/
Perhaps your two projects could merge somehow?
That looks really cool – I plan to modularise my code base at some point so that you can plug-in different sensor modules/scripts to the Raspberry Pi server – might take a look then. Cheers.
Tom, Thanks for your work on this. I was able to jump start my own Pi Therm Monitor.
I have extended your work, to include historic reporting. Let me know if you would like to Merge our projects.
https://github.com/jschappet/PiThermServer
http://home.schappet.com:7000/historic.html — Historic View
Hi Jimmy, great to hear it’s useful. I’ve been working a function to log temperatures to an SQLite database, to generate a time series. I’ve had this running on my Pi outside for over a week. When I get chance to push updates to github, I agree, we should look at merging.
Cheers
Tom
Hi Tom,
Thanks for your nice Temperature Server(with temperature_log).
I installed nodejs and sqlite3 with apt-get and now I’m trying your server.js.
After build_database.sh I got a node_modules folder.
But sudo nodejs server.js says:
module.js:337
throw new Error(“Cannot find module ‘” + request + “‘”);
^
Error: Cannot find module ‘../build/Release/node_sqlite3.node’
at Function._resolveFilename (module.js:337:11)
at Function._load (module.js:279:25)
at Module.require (module.js:359:17)
at require (module.js:375:17)
at Object. (/home/pi/PiThermServer/node_modules/sqlite3/lib/sqlite3.js:1:104)
at Module._compile (module.js:446:26)
at Object..js (module.js:464:10)
at Module.load (module.js:353:32)
at Function._load (module.js:311:12)
at Module.require (module.js:359:17)
How can I solve this problem?
Cheers
Marcus
Hi Marcus,
You need to install the Sqlite 3 module for node is. Check it out here: https://github.com/developmentseed/node-sqlite3
Once you do the server script should work. However, you may also like to check that you have the node-static module installed as well. You can see the requirements for different modules at the top of server.js but all the rest should be included as standard.
I hope it works!
Cheers,
Tom
The site for the node-static module is here: https://github.com/cloudhead/node-static
Hi Tom,
Thanks, but I did
npm install sqlite3
npm install note-static
But there is no file node_sqlite3.node???
Cheers,
Marcus
I have solved it!
npm install sqlite3 doesn’t work.
I had to do this way:
npm install -g node-gyp
git clone git://github.com/developmentseed/node-sqlite3.git
cd node-sqlite3
./configure
make
Thanks for the fantastic temperature plot!
Cheers,
Marcus
Marcus – that’s great! I may add your comments to the readme on github, to make it easier for others in the future. Cheers.
Thanks for the great tutorial. I am trying to get it to work with an existing MySQL database. It’s basically working, but I am having problems with JS timestamps. If I do something like:
“SELECT pressure, timestamp FROM observations LIMIT 50;”,
My observations are all clustered around Jan 1st 1970, which is what I would expectas MySQL timestamps are not in nix time format. If I try and convert MySQL timestamps into JS timestamps:
“SELECT pressure, UNIX_TIMESTAMP(timestamp)*1000 FROM observations LIMIT 50;”
the results appear correct when I run the mysql command in a terminal, but HighCharts doesn’t display any data. Any clues as to what I might be doing wrong?
To answer my own question:) My SQL is very rusty I needed:
“SELECT pressure, UNIX_TIMESTAMP(timestamp)*1000 AS timestamp FROM observations LIMIT 50;”
When I have tidied it all up I’ll pust it on Github.
Hi Ian, thanks for your comment. Glad you got it sorted.
Tom
Hi Ian, thanks for posting this. Did you ever get the MySQL modification posted to Github? I’m reasonably comfortable with simple MySQL stuff but new to javascript and node.js, so if you could show me some tested code I’d be grateful.
Someone tell me step by step , how can i install this ? 😦
Hi, I’ll try my best:
1) install nodejs on your raspberry pi. See this post:
2) install sqlite3 database package: sudo apt-get install sqlite3
3) install the nodejs sqlite3 module: sudo npm install sqlite3
See here for more info: https://github.com/developmentseed/node-sqlite3
4) download and unzip the nodejs temperature scripts from github:
5) in the extracted folder open a terminal and run the “load_gpio.sh” script as root. For example: sudo ./load_gpio.sh
This should load the kernel modules required to read from the sensor. You need to do this after every reboot unless you add the modules to be loaded at boot.
6) in the extracted folder open a terminal and run “build_database.sh” This will create a new database “piTemps.dB” (overwriting any previous database of the same name in that folder).
7) open server.js and edit line 35 to read the serial number of your thermometer in /SYS/bus. Check out step 3 of this tutorial for explanation:
Save server.js
8) open a terminal in the folder and run “node ./server.is”. You should see messages telling you the database and server are running. Leave this terminal window open (closing it will stop the server).
9) if everything’s worked, open a web browser on your pi and go to: http://localhost:8000/temperature_plot.htm to see a plot of current temperature. Go to http://localhost:8000/temperature_log.htm to see a plot of logged temperature.
I hope that helps 🙂
Oh god , finally 🙂 thank you so much and Marcus
No worries, I hope that helps! I’ve added a synopsis of setup steps to the GitHub readme.
Unfortunately a few of the links in my answer didn’t paste properly. Here they are.
Installing NodeJS: http://www.raspberrypi.org/phpBB3/viewtopic.php?f=34&t=18775
GitHub link:
https://github.com/talltom/PiThermServer
Reading from the sensor (serial numbers):
http://www.cl.cam.ac.uk/freshers/raspberrypi/tutorials/temperature/#step-three-a
Hi, great work ! Your temperature server is excellent !!
But I have one problem:
on my raspberry is 10:30 but the graph is at 8:30. Chart uses the UTC time that is two hours less than my (I live in Poland). Is it possible to move the time of two hours in the chart?Thank you very much and sorry for my terrible english;)
Hi Michael – I’m glad you like it 🙂
If you edit temperature_log.htm you can account for the time zone difference. At line 23 add the following lines to get the timezone difference:
OK, now you need to edit the while loop below to push the JSON data with updated time to the plot (add tz to unix_time).
This should give your x-axis in local time. Let me know if it works!
Tom
hi,thanks for the indication of finding a solution.
I added the line:
var tz = date.getTimezoneOffset () * 60000 / / one zero more because (sec in hour * ms in sec ==> (60 * 1000))
I had to also add the brackets, and replace the + sign on – because the value of tz has negative sign:
series.data.push ([(data.temperature_record [0] [i]. unix_time) – tz, data.temperature_record [0] [i]. celsius]); i + +; }
Thanks for your help
greetings
Michael
fix:
var tz = date.getTimezoneOffset () * 60000 / / one zero more because (sec in min * ms in sec ==> (60 * 1000))
Great! I’m glad you got it working.
Tom,
Your work is absolutely appreciated! After about a half day of work I have a basic system working. I set things up to use a mysql database instead of sqlite3. I just have a couple questions that hopefully you have insight on:
1) I installed the node-static module with the -g option:
npm install -g node-static
which places the files in /usr/local/lib/node_modules. When I try to include this:
var nodestatic = require(‘/usr/local/lib/node_modules/node-static’);
I have to include the full path. Do you know if there is an environment variable I need to set? (by the way, I have to do the same thing with the db-mysql module).
2) When I type in the address of the server:
http://192.168.1.5:8000/temperature_query.json
my browser shows the JSON return string instead of plotting the data. I’m sure I am missing something but not sure what. The HTML code is identical to what you have posted. I am using Apache which is running on port 80 so if I enter the address http://192.168.1.5:8000 I get my regular web page.
Hi Keith – thanks, nice to hear it’s useful.
I’m not sure about your node modules path issue I’m afraid – there may be a way to add ‘/usr/local/lib/node_modules’ to your path, but you’ll have to Google it. Let me know if you find an answer.
You need to point your browser to http://192.168.1.5:8000/temperature_log.htm or temperature_plot.htm to see the plots. The .json addresses are just the data output from the server.
Cheers and good luck!
Tom
Hi Keith,
Any chance of you sharing your files for using this with MySql instead of sqlite3?
I’m not good with either, but got MySql database running, but I do not know what to change in the files to get it running with mysql.
regards, Bor
Thanks Tom. I did get it figured out :-). I was expecting my Apache server to handle the HTML part but it turns out the nodejs server should be serving that page as well. I’ll do more research on the path thing and post if I find a solution. Now it’s time to learn the HighCharts APIs!
Later,
-Keith
How about sudo apt-get install sqlite3 to install sqlite3
Thank for your work, I’m very new to javascript but I make it worked. It would be great if you add “range selector” to highcharts. I think “dynamic-master-detail” chart http://www.highcharts.com/demo/dynamic-master-detail ,but still trying to get it worked.
Thanks for the script, this is exactly what I was looking for! I am have a problem through (probably because I am very new to all of this). I have installed the prereq’s (node.js, sqlite3, etc), and have built the database (the pitemps.db file is there after running the build script), but when I try to run the server.js script I get the following:
^
Error: Cannot find module ‘sqlite3’
at Function._resolveFilename (module.js:337:11)
at Function._load (module.js:279:25)
at Module.require (module.js:359:17)
at require (module.js:375:17)
at Object. (/usr/share/adafruit/webide/repositories/my-pi-projects/ThermServer/server.js:16:15)
at Module._compile (module.js:446:26)
at Object..js (module.js:464:10)
at Module.load (module.js:353:32)
at Function._load (module.js:311:12)
at Array.0 (module.js:484:10)
As the build_database.sh script worked and I see the pitemps.db file, I am assuming that sqlite3 is working. What could be causing this issue? Any help would be greatly appreciated.
– Josh
Though it was a different error, I tried installing the node-sqlite module using npm as per the user above who had a similar error. When I issue the make command, I get the following:
make[1]: Entering directory `/node-sqlite3/build’> ACTION deps_sqlite3_gyp_action_before_build_target_unpack_sqlite_dep deps/sqlite-autoconf-3071700/sqlite3.c
>
> module.js:337
> throw new Error(“Cannot find module ‘” + request + “‘”);
> ^
> Error: Cannot find module ‘/node-sqlite3/node_modules/.bin/targz’
> at Function._resolveFilename (module.js:337:11)
> at Function._load (module.js:279:25)
> at Array.0 (module.js:484:10)
> at EventEmitter._tickCallback (node.js:190:39)
> make[1]: *** [deps/sqlite-autoconf-3071700/sqlite3.c] Error 1
> make[1]: Leaving directory `/node-sqlite3/build’
> gyp ERR! build error
> gyp ERR! stack Error: `make` failed with exit code: 2
> gyp ERR! stack at ChildProcess.onExit (/usr/local/lib/node_modules/node-gyp/lib/build.js:267:23)
> gyp ERR! stack at ChildProcess.emit (events.js:70:17)
> gyp ERR! stack at maybeExit (child_process.js:362:16)
> gyp ERR! stack at Process.onexit (child_process.js:398:5)
> gyp ERR! System Linux 3.1.9adafruit+
> gyp ERR! command “node” “/usr/local/bin/node-gyp” “build”
> gyp ERR! cwd /node-sqlite3
> gyp ERR! node -v v0.6.19
> gyp ERR! node-gyp -v v0.9.6
> gyp ERR! not ok
> make: *** [build] Error 1
Am I getting anywhere? I don’t know how to correct the above error, but I am now thinking my issue has to do with node-sqlite instead of sqlite3.
-Josh
Hi Josh,
I’ve never built node-sqlite3, as npm worked for me in the past. Have you looked at the build instructions on https://github.com/developmentseed/node-sqlite3#building ?
Looks like there may be some dependencies to install first.
Let me know how you get on.
Tom
All this work and I have a wonderfully working temperature logger in Celsius… What does a guy in the US have to do to change it to Fahrenheit? Any help would be much appreciated. Thanks!
Adopt the metric system? 😉 I’m only kidding. You could tweak server.js – to write the values in Fahrenheit, but you’ll need to check the database structure and .htm files, as I think I use celsius as a hard coded column name.
I’m a rank novice with node.js and not all that familiar with Javascript either. Can you advise me on the “correct” asynchronous way to re-read in case invalid data is encountered? Occasionally my sensor returns “no” instead of “yes” at the end of the first line of data. I have no troubles identifying that return value but I can’t figure out how to re-read from the sensor without screwing up the intent of asynchronous i/o. Thanks for your suggestions.
Actually – maybe this will work. If you inspect the return string for Yes/No then you can modify the callback return at the end.
if (valid) callback(data) else readTemp(callback);
Does that sound correct?
Yes, that sonds about right. Let me know how you get on.
So it seems fine. Thanks for the intro to node.js!
Where exactly should I put those extra line? How should it looks like? I’m not familiar with JS and can’t deal with ‘if’ condition.
Hi!
I want to add second sensor and add series to the chart. What should I do? Change the server.js to read second value, save it in changed database and then in .htm file add series? Can anyone give me the step-by-step guide how to do that?
Thanks for your help!
Hi, Looking to do the same with six sensors, Adafruit’s guide states you can add more sensors in series, I need some guidance with the coding, six sensors overlaying on one graph, colour coded for each sensor or one graph per sensor. Thanks.
Hello,
after following the steps installing nodejs, sqlite3, node-sqlite3 and node-static modules get this error and I do not know how to fix it.
node ./server.js
Server is logging to database at 300000ms intervals
Server running at http://localhost:8000
{ [Error: EISDIR, read] errno: 28, code: ‘EISDIR’ }
help please
[…] Temp (inte för att mäta temperatur nödvändigtvis). […]
Tom
This is excellent. Tried a few other solutions but find this one the most elegant, well documented and simplest. Many thanks for sharing it. My rabbit is very grateful too. http://www.top-bun.com/temp
Paul
very nice project but
i can’t install anything always gettin errors , i use raspbian 09.09.14
any solution ?
Dear Tomas,
thank you for this fantatic project. Works out of the box (after some hiccups with dependencies on my end). Have failed with braubuddy, devicehive and many others when trying to install them. Your efforts saved me lots of dreamless nights 🙂
Hi,
great stuff, works really nicely! I’m trying to add a second sensor as well but i can’t get the server code right (i can read the value from the second sensor but can’t get it into the database – probably because i have zero javascript/nodejs knowledge. Can anyone help me out?
Thanks!
G
Nice answer back in return of this question with
firm arguments and telling the whole thing on the topic of that.
This is awesome – love it. Tomas (or anyone else), have you had an occasion to split the collector from the webserver? I am wanting to put the thermostats inside a corporate firewall that only allows ssh out, so I would put the webserver outside the firewall. Thus I would want to break this into two pieces. I don’t think it would be that hard, just wondering if anyone else has already done it before I dive in.
Love what you are doing. Thanks for putting this out there.