Raspberry Pi CPU temperature visualization

RPI cpu temperature

Raspberry Pi has onboard temperature sensors (for CPU and GPU). We came to idea to continuously read the value of CPU temperature and show it on a web page. The temperature sensor can be directly accessible through the sys file system in Raspbian. Therefore, the main work was to make constant updating of the web page where we would like to show the current CPU temperature. We have added additional temperature sensor (DS18B20) which we fixed to the CPU (with high temperature tape) to be sure about the readings that we are getting from the onboard temperature sensor. DS18B20 sensor has three pins - 3.3V, GND and data line which is connected to the GPIO4. Once you have connected these pins to the GPIO header of the Raspberry Pi, you will also need to put one pull resistor (4k7) between data line and 3.3V. The rest of the details about this very popular digital temperature sensor can be found here

To constantly update web page, we decided to use websocket protocol. If you are not familir with websocket, please read this article where simple example is presented. The idea is to read the CPU temperature every 2 seconds and then send this data to a web page using websockets. For temperature visualization on client side we used RGraph library for JavaScript.

At first you have to install Tornado, an asynchronous webserver for python:

pip install tornado

Our application will read the temperature sensors and send the temperature values over websocket to the web page in the following structure: "DS18B20_reading ; onboard_reading". Therefore, first write function in python which reads temperature sensors and makes the message to be sent:

def read_temp():
  try:
    #replace xxxxxxxxxxxx with code of your DS18B20
    file = open('/sys/bus/w1/devices/28-xxxxxxxxxxxx/w1_slave', 'r')
    lines = file.readlines()
    if lines[0].find("YES"):
      pok = lines[1].find('=')
      temp = str(float(lines[1][pok+1:pok+6])/1000)
    file.close()

    #read temp. onboard sensor    
    file = open('/sys/class/thermal/thermal_zone0/temp','r')
    lines = file.readlines()
    temp += ';' + str(float(lines[0])/1000)
    file.close()
    wsSend(temp)
  except:
    wsSend("-;-")

As you can see, after both sensors are read the message is sent over websocket with "wsSend" function which has the following structure (we found this solution somewhere on stackoverflow):

def wsSend(message):
  for ws in wss:
    if not ws.ws_connection.stream.socket:
      print "Web socket does not exist anymore!!!"
      wss.remove(ws)
    else:
      ws.write_message(message)

The sensor readings are performed periodically, every 2 seconds for which we have used PeriodicCallBack loop that is available in Tornado. Apart from that, we would like to print info to web page when websocket connection is established or disconnected which is defined in WShandler. Therefore, the whole python scripts named wsRPiTemp.py looks like this:

#!/usr/bin/env python
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import os

wss =[]
class WSHandler(tornado.websocket.WebSocketHandler):
  def open(self):
    print 'New user is connected.\n' 
    if self not in wss:
      wss.append(self)
  def on_close(self):
    print 'connection closed\n'
    if self in wss:
      wss.remove(self)

application = tornado.web.Application([(r'/ws', WSHandler),])

if __name__ == "__main__":
  os.system('modprobe w1-gpio')
  os.system('modprobe w1-therm')
  interval_msec = 2000

  def wsSend(message):
    for ws in wss:
      if not ws.ws_connection.stream.socket:
        print "Web socket does not exist anymore!!!"
        wss.remove(ws)
      else:
        ws.write_message(message)

  def read_temp():
    try:
      #replace xxxxxxxxxxxx with code of your DS18B20
      file = open('/sys/bus/w1/devices/28-xxxxxxxxxxxx/w1_slave', 'r')
      lines = file.readlines()
      if lines[0].find("YES"):
        pok = lines[1].find('=')
        temp = str(float(lines[1][pok+1:pok+6])/1000)
      file.close()

      #read temp. onboard sensor    
      file = open('/sys/class/thermal/thermal_zone0/temp','r')
      lines = file.readlines()
      temp += ';' + str(float(lines[0])/1000)
      file.close()
      wsSend(temp)
  except:
    wsSend("-;-")

  http_server = tornado.httpserver.HTTPServer(application)
  http_server.listen(8888)
    
  main_loop = tornado.ioloop.IOLoop.instance()
  sched_temp = tornado.ioloop.PeriodicCallback(read_temp, interval_msec,   io_loop = main_loop)

  sched_temp.start()
  main_loop.start()

The server is started with the following command:

sudo python wsRPiTemp.py &

or if you want to run it when the Pi boots, put that command in /etc/rc.local file.

Now we can proceed to the html page where the temperatures will be shown. This page should be put somewhere in /var/www directory if you are using Apache web server. The page consists of three <label> and two <canvas> elements. One label shows the current status of websocket connection and other two labels are showing readings from DS18B20 and onboard temperature sensors. In canvas elements the JavaScript will print the thermometers that are showing temperature readings. Inside Javacript the instance of WebSocket class is created. This object handles all requests for our app with three event handlers: onopen, onclose and onmessage. The onmessage handler receives the messages that contain sensors readings, print these values to the labels and make two thermometers. The whole page should look like this:

<!doctype html>
<html>
<head>
<title>My Raspberry Pi CPU temperature</title>
<script src="http://code.jquery.com/jquery-2.0.0.js"></script>
<script type="text/javascript" src="/javascript/RGraph.common.core.js" ></script>
<script type="text/javascript" src="/javascript/RGraph.thermometer.js" ></script>
</head>
<body>
<h2>DS18B20 vs onboard temperature sensor</h2>
<label id="conn_text"></label><br />
<br>
<label id="ds18b20_temp">DS18B20: </label><br />
<label id="cpu_temp">CPU temp: </label><br />
<br>
<br>
<canvas id="cvs_E" width="150" height="200">[No canvas support]</canvas>
<canvas id="cvs_CPU" width="150" height="200">[No canvas support]</canvas>

<script>

  $(document).ready(function () {
    var ws = new WebSocket("ws://example.com:8888/ws");

    ws.onopen = function(evt) {
      var conn_status = document.getElementById('conn_text');
      conn_status.innerHTML = "Status: Connected!"
    };
                
    ws.onmessage = function(evt) {
      var ds18b20_reading = document.getElementById('ds18b20_temp');
      var cpu_reading = document.getElementById('cpu_temp');
      current_temp = (evt.data).split(";");			
      ds18b20_reading.innerHTML = "DS18B20: " + current_temp[0];                        
      cpu_reading.innerHTML = "CPU sensor: " + current_temp[1];
      drawGraph_environment();
      drawGraph_CPU();
    };

    ws.onclose = function(evt) {
      var conn_status = document.getElementById('conn_text');
      conn_status.innerHTML = "Status: disonnected!"
    };
  });

  function drawGraph_environment(){
    var canvas = document.getElementById('cvs_E');
    RGraph.Clear(canvas);

    var thermometer = new RGraph.Thermometer('cvs_E', 0,100,     eval(current_temp[0]))     	
    .set('title.side', 'Environment')
    .set('value.label', false)
    .set('scale.visible', true)
    .set('scale.decimals',2)
    .set('gutter.left', 80)
    .set('gutter.right', 40)
    .draw();
  }
	
  function drawGraph_CPU(){
    var canvas = document.getElementById('cvs_CPU');
    RGraph.Clear(canvas);

    var thermometer = new RGraph.Thermometer('cvs_CPU', 0,100,     eval(current_temp[1]))
    .set('title.side', 'RPi CPU')
    .set('value.label', false)
    .set('scale.visible', true)
    .set('scale.decimals',2)
    .set('gutter.left', 80)
    .set('gutter.right', 40)
    .draw();
  }
	
</script>
</body>
</html>

Make sure that you replace "example.com" with IP address of your Raspberry Pi. Apart from that, RGraph library have to be somewhere on your Pi; check if you entered right path to this Javascript library in body of your html page. The resulting page looks like the image below (browser must support canvas element). It appears that my DS18B20 and onboard temperature sensors have similar readings (difference is about 2 degrees Celsius).

Only your imagination is your limit. If you don't like this thermometer, you can use some other gauge from the library or even make your own. Connect more sensors to the Raspberry Pi, make dynamic graphs, add butons to control output pins on your Pi and so on...

Share:  Add to Facebook Tweet This Add to Delicious Submit to Digg Stumble This