Reporting Rasp Pi data on a Node.js web server

DHT on a Web Page

At this stage, you could just print out the results as text to a web page (Listing 5), combining what you learned from Listing 2 with what you saw in Listing 4. However, that's boring, so do it visually instead.

Listing 5

server.js (I)

01 var http = require("http");
02 var sensorLib = require("node-dht-sensor");
04 http.createServer(function (request, response)
05 {
06    var sensorResult =, 18);
07    var resultTH="Temperature: " + sensorResult.temperature.toFixed(1) + "° C<br />Humidity:  " + sensorResult.humidity.toFixed(1) + "%";
09    response.writeHead(200, {'Content-Type': 'text/html'});
10    response.end(resultTH);
11 }).listen(8081);
13 console.log("Server running on port 8081");

By using CSS and a template, you can create a graphical representation for your sensor (Figure 5). Although Node.js has several modules that allow you to create HTML templates, you can create your own using the code in Listing 6.

Listing 6


01 <html>
02   <head>
03   </head>
04   <body>
06     <h2>Temperature</h2>
07     <div style="margin-left: 200px;">
08       0&deg; C
09     </div>
10     <div style="margin-left: 202px; height: 0.5em; width: 0px; border-style: solid; border-width: 1px;">
11     </div>
13     <div style="display: flex; width: 1000px;     border-style: solid; border-width: 2px;">
14       <div style="width: {0}px;">
15       </div>
16       <div style="width: {1}px; background-color: {2}; text-align:{3}; color:white;">
17         {4}&deg; C
18       </div>
19     </div>
21     <h2>Humidity</h2>
22     <div style="display:flex; width:1004px">
23       <div style="margin-left: 0px;">
24         0%
25       </div>
26       <div style="width:1000px;">
27       </div>
28       <div style="margin-left: 0px;">
29         100%
30       </div>
31     </div>
33     <div style="display:flex; width:1004px">
34       <div style="height: 0.5em; width: 0px; border-style: solid; border-width: 1px;">
35       </div>
36       <div style="width:1000px;">
37       </div>
38       <div style="height: 0.5em; width: 0px; border-style: solid; border-width: 1px;">
39       </div>
40     </div>
42     <div style="width: 1000px;  border-style: solid; border-width: 2px;">
43       <div style="width: {5}; background-color:green; text-align:right; color:white;">
44         {6}%
45       </div>
46     </div>
47   </body>
48 </html>
Figure 5: With CSS and an HTML web page template, you can create a graphical representation for the values read from your sensor.

You will notice several numbers in curly brackets (e.g., {0}, {1}, {2}, …) peppered around the markup, which you will seek out and change to values you calculate from your DHT sensor readout. Take a look at Listing 7 to see how this works.

Listing 7

server.js (II)

01 var http = require("http");
02 var sensorLib = require("node-dht-sensor");
03 var fs = require("fs");
05 http.createServer(function (request, response)
06 {
07   fs.readFile('./bars_template.html', 'ascii', function(err, html_string)
08   {
09     if (err)
10     {
11       response.writeHead(500);
12       response.end("<h1>Error 500</h1>");
13     }
14     else
15     {
16       var sensorResult =, 18);
17       response.writeHead(200, {'Content-Type': 'text/      html'});
18       response.end(build_string(html_string.toString(), sensorResult.temperature.toFixed(1), sensorResult.humidity.toFixed(1)));
19     }
20   });
21 }).listen(8081);
23 console.log("Server running on port 8081");
25 function build_string(html_str, temperature, humidity)
26 {
27   var result_array=[];
28   var MAX_TEMP=40;
29   var MIN_TEMP=-10;
31   if (temperature > MAX_TEMP) { temperature = MAX_TEMP; }
32   if (temperature < MIN_TEMP) { temperature = MIN_TEMP; }
34   var R = parseInt(255 * ((temperature - MIN_TEMP) / (MAX_TEMP - MIN_TEMP)));
35   var G = 0;
36   var B = 255-R;
38   result_array[0]=200;
39   result_array[3]="right"
41   if (temperature<0)
42   {
43     result_array[0]=200+(temperature*20);
44     result_array[3]="left"
45   }
47   result_array[1]=Math.abs(temperature)*20;
48   result_array[2]= "rgb(" + R + "," + G + "," + B + ")";
49   result_array[4]=temperature;
50   result_array[5]=humidity*10;
51   result_array[6]=humidity;
53   var temp="";
55   for (i=0; i<result_array.length; i++)
56   {
57       temp=html_str.replace("{"+i.toString()+"}", result_array[i].toString());
58       html_str=temp;
59   }
61   return (html_str);
62 }

Starting from the top of Listing 7, lines 1-3 bring in the modules you need to make your server work. Notice on line 3 that you load the fs module, which is built into Node.js (i.e., you don't need to install it) and manages files and filesystems.

On line 7, right after starting the server proper, you put fs to work by trying to open your template file, bars_template.html. The second argument tells readFile() what encoding to expect the data in that file to use – in this case I want to load it in as a simple string of ASCII characters. The third argument specifies the callback function, which dumps the content of the file as one long string into html_string. If there is an error reading the file (lines 9-13) (e.g., you misspelled the file name), you throw a 500 error (Internal Server Error) and show an error message on the page.

If the file loads correctly, the server reads the data from the sensor (line 16), sends the 200 status code (OK) back to the browser (line 17), and then sends the HTML string as a page to the petitioning browser (line 18).

To build the HTML string, you call the build_string() function (lines 25-62). This function takes the character string containing the contents of the template file (html_str), the temperature, and the humidity as arguments.

You then set up an array (line 27) that is filled with the values you are going to switch into your template. Therefore, the contents of result_array[0] will take the place of {0}, result_array[1] will substitute in {1}, and so on.

For example, with {0}, because you can have negative or positive temperatures, 0°C is not at the beginning of the bar (see Listing 6, line 16; Figure 5). If your temperature is over 0°C, you have to insert an empty <div> that measures 200 pixels long (Listing 7, line 38). In this way, the above zero temperature bar starts at the 0°C mark and stretches off to the right, as you can see in Figure 5.

However, if the temperature is below zero, as in Figure 6, the temperature bar needs to stretch from the 0°C point to the left. To do that, you make the empty <div> shorter (Listing 7, line 43) and then align the temperature bar next to it.

Figure 6: If the temperature is below 0°C, the bar must stretch toward the left.

Table 1 shows the rest of the values substituted into the placeholders and what they do.

Table 1

Placeholder Substitution Table


Substituted Data


Space before the temperature bar; e.g., 200


Length of temperature bar


Color of temperature bar; e.g., rgb(153, 0, 102). The lower the temperature, the bluer the bar; as temperatures rise, the bar becomes redder (Figures 5 and 6).


Horizontal alignment of text on the temperature bar




Length of humidity bar



The code in lines 55-59 (Listing 7) iterates over the array and uses the JavaScript replace() function to substitute the placeholders in the HTML string read in from bars_template.html.

Try it out by running the server with

npm start

and visit your Rasp Pi's IP on port 8081 as before (i.e., http://<IP address>:8081). You'll see a graphical representation of the temperature and humidity at the location of your sensor.


Node.js is a great tool for Rasp Pi makers! It provides an easy and well-tested way of turning your Pi into an appliance that can serve up all sorts of information on a web page. Its immense catalog of modules makes creating complex web applications fast and painless.

If you would like to learn a bit more about Node.js, I wrote a two-part introduction for beginners for [4].

Buy this article as PDF

Express-Checkout as PDF

Pages: 8

Price $2.95
(incl. VAT)

Buy Raspberry Pi Geek

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content