Plotting sensor data using GnuPlot

Data of all sensors attached to my Pi are collected in a MySQL database. There are several ways of generating plots from sensor data such as jgraph. However, for a machine with a CPU frequency of 700 MHz, it is probably not the best idea to generate plots “on-the-fly”. Therefore, I decided to use GNUplot for generating plots of collected data sets at fixed time-intervals in order to present them as PNG images on a static webpage.

All you have to do is to export the desired data set into a CSV file, e.g. data of the past 24 hours and to run GNUplot on it. The MySQL export and GNUplot commands can be placed in a single bash script, which can be  run as a cron job. Here is a short tutorial:

Data export: MySQL database to CSV file

In order to work with the data on an other machine it is necessary to export the desired subset from the database. Unfortunately, the INTO OUTFILE statement of MySQL doesn’t allow to choose a remote server for saving the CSV file. Therefore, one has to redirect the MySQL output. For replacing the tab-stops with a different separator, one can pipe the data stream through a stream editor such as ‘sed’:

mysql -h servername -u username --password=secret --skip-column-names -e "SELECT * FROM table where FROM_UNIXTIME(timestamp) >=NOW() - INTERVAL 7 DAY;" | sed 's/\t/;/g;s/\n//g' > week.csv

As a result a CSV file containing a subset of observations from the last 7 days is obtained.

2014-02-22;13:13:00;1393074780;20.3;47.4;21.4;1004.64;71.51;117.9;9.1
2014-02-22;13:14:00;1393074840;20.3;47.5;21.4;1004.73;70.84;119.8;9.1
2014-02-22;13:15:00;1393074900;20.3;47.4;21.4;1004.82;70.93;117.6;8.9
2014-02-22;13:16:00;1393074960;20.3;47.6;21.4;1004.7;71.68;117.8;9 
2014-02-22;13:17:00;1393075020;20.3;47.5;21.4;1004.73;72.18;121.5;9 
2014-02-22;13:18:00;1393075080;20.3;47.5;21.4;1004.69;71.68;126.3;9 
2014-02-22;13:19:00;1393075140;20.3;47.7;21.4;1004.69;72.02;125.6;9.1 
2014-02-22;13:20:00;1393075200;20.3;47.7;21.4;1004.67;70.93;123.4;9.1 
2014-02-22;13:21:00;1393075260;20.3;47.7;21.4;1004.7;71.26;124.4;9.1
.
.
.

Shell script to plot data of a CSV file using GNUplot

The columns in this file consist of date, time, timestamp, tmp1, hum, tmp2, bar, alt, light, tmp3. Now you are ready to plot the data using GNUplot. This can be done with a simple shell script:

#!/bin/bash

# Define title of the plot and print last date of plotting
TITLE="Temperature, Relative Humidity and Pressure until `date +%F\ %T`"

# To correct UNIX epoch date and gnuplot epoch date, we have to substract $EPOCH_OFFSET
EPOCH_OFFSET=946684800

# Get UNIX epoch date
TODAY="`date +%s`"

# Correct offset to 2000-01-01 00:00:00
TODAY=$(expr $TODAY - $EPOCH_OFFSET)

# We want to plot values from 24 h
YESTERDAY="`date +%s --date="-1 day"`"

# Correct offset to 2000-01-01 00:00:00
YESTERDAY=$(expr $YESTERDAY - $EPOCH_OFFSET)

/usr/bin/gnuplot << EOF

# Data file uses semikolon as a separator
set datafile separator ';'

# Title of the plot
set title "$TITLE"

# We want a grid
set grid

# Ignore missing values
#set datafile missing "NaN"

# Functions for dew point,vapour pressure and g/m3
svp(t)=610*exp(t/(t+238.3)*17.2694)
vp(t,rh)=svp(t)*rh/100
w(t,rh)=log(vp(t,rh)/610.78)
dpt(t,rh)=(w(t,rh)*238.3)/(17.294-w(t,rh))
gm3(t,rh)=vp(t,rh)*2.166/(t+273.16)

# X-axis label
set xlabel "Date/Time (CET)"

# set X-axis range to current date only
set xrange ["$YESTERDAY":"$TODAY"]

# Y-axis ranges from 0 deg C to 100 deg C, same scale as for humidity
set yrange [0:80]

# Y2-axis set from 800 to 1200 kPa, used for barometric pressure
set autoscale y2

# place ticks on second Y2-axis
set y2tics border

# Title for Y-axis
set ylabel "Temperature (C) / Relative Humidity (%)"

# Title for Y2-axis
set y2label "Pressure (hPa)"

# Define that data on X-axis should be interpreted as time
set xdata time

# Time in log-file is given in Unix format
set timefmt "%s"

# Display notation for time
set format x "%d.%m\n%R"    # Display time in 24 hour notation on the X axis

# generate a legend which is placed underneath the plot
set key outside bottom center box title "RasPi Sensor Data"

# output into png file
set terminal png large
set output "/var/www/images/plot.png"

# Data columns are:
# 3 is Unix Epoch time
# 4 is Temperature DHT22
# 5 is Humidity DHT22
# 6 is Temperature BMP085
# 7 is Barometric Pressure
# 8 is Altitude

# read data from file and generate plot
plot "/var/www/data/week.csv" using 3:4 title "Temperature (C)" with lines \
 ,"" using 3:5 title "Relative Humidity (%)" with lines \
 ,"" using 3:7 title "Barometric Pressure (hPa)" with lines axes x1y2 \
 ,"" using 3:(dpt(\$6,\$5)) title "Dew Point (C)" with lines\
# ,"" using 3:(gm3(\$6,\$5)) title "Dew Point (C)" with lines\
# end of script
EOF

It is important to note that the timestamp used by GNUplot differs from the UNIX EPOCH time variable which starts at 1970-01-01 00:00:00. For GNUplot it is necessary to correct the offset date to 2000-01-01, otherwise the x-axis of your plots will display wrong labels.

Plots can be generated in different file formats. More results can be visited on my Sensors page.

Update

If you’d like to run the above script on your RasPi server, you can call an SQL query within the GNUplot shell script above. Just place the following line to the top of the script.

sudo mysql -uuser -p password -D database -B -e "SELECT * FROM sensors WHERE FROM_UNIXTIME(timestamp) >= NOW() - INTERVAL 7 DAY INTO OUTFILE '/tmp/week.csv' FIELDS TERMINATED BY ';' LINES TERMINATED BY '\n'"

Please note that MySQL has permissions to write only into the /tmp folder on Debian Wheezy.  Remember that MySQL doesn’t overwrite week.csv if it already exists!