Indoor and outdoor temperature measurements on a 24h time period.
A project using thermistors for temperature sensing, arduino for data acquisition, memcached for short term-storage and mathplotlib for plotting temperature variations.
Every second, arduino measures voltage across a voltage divider made of negative temperature coefficient thermistor (NTC) taken from a salvaged indoor/outdoor thermometer and a 5k6 resistor. (See diagram temperature_monitor_diagram.png in Samples directory).
The NTC and the resistor are connected in series, between the Arduino 5v reference voltage and the ground. A wire connected at the junction of the NTC and the resistor is also connected to an analog input of the arduino.
This circuit can be repeated to have more temperature sensors if desired. I chose to use two, one for indoor temperature, one for outdoor temperature.
NTCs have a non-linear temperature to resistance relationship, which can be modelized with a third order polynomial.
In order to figure out the coefficients of the polynomial, you have to measure the resistance of the NTC at different temperatures, using an ohmmeter, a freezer, some ice, water that you will heat and a reference thermometer. These are the readings I got. Yours will probably be different.
T(c) | Rntc(Ko) |
---|---|
-18 | 48.8 |
-15 | 42 |
0 | 24 |
20 | 12.4 |
40 | 5.4 |
50 | 4 |
60 | 2.8 |
70 | 2.15 |
80 | 1.65 |
90 | 1.34 |
100 | 1.01 |
Now the resistance values are fine, but the arduino measures voltages, not resistances. You must convert the resistance readings in voltage values as seen by the arduino, applying Ohm's Law to a voltage divider network, where the output voltage measured across the series resistor is
Vo = Vref * (R /(Rntc+R))
where Vref is the reference voltage taken from the arduino (5 volts), Rntc is the measured resistance, and R is the series resistor (5.6Ko).
Using this formula, you can determine Vo for all Rntc values.
T(c) | Rntc(Ko) | Vo |
---|---|---|
-18 | 48.8 | 0.51 |
-15 | 42 | 0.59 |
0 | 24 | 0.95 |
20 | 12.4 | 1.56 |
40 | 5.4 | 2.55 |
50 | 4 | 2.92 |
60 | 2.8 | 3.33 |
70 | 2.15 | 3.61 |
80 | 1.65 | 3.86 |
90 | 1.34 | 4.03 |
100 | 1.01 | 4.24 |
The resistor is chosen in order to have a voltage range that is roughly linear in the target temperature range, and total resistance should be around 10k for optimal results with the arduino.
In my case, 5k6 gave the best results.
Using Vo as the independant variable and T as the dependant variable, we can now find a third order polynomial describing our system.
The polynomial that best fits this dataset is
T = 3.296v^3 -22.378v^2 +70.951v -49.382
(See diagram ntc_measurements_and_polynomial_regression.png in Samples directory).
You can find online polynomial regression tools to determine these coefficients at http://www.xuru.org/rt/pr.asp
Every second, the arduino measures the voltages at one of the two analog inputs, and sends a temperature reading to the usb port, converting the voltage values in temperature values using the polynomial equation, and indicating which input was read.
Saving the data every second does seem like overkill, but the readings vary slightly on every reading.
I chose to average the data sent on a one minute period, to get a less noisy measurement, and then save the averaged value in memory. I chose memcache as the data storage because the data does not require a long conservation time.
A one day period seems enough for reporting purposes. With two inputs and a measurement every minute, it requires to store 2880 measurements per day.
A longer period would probably use a database to store the potentially ever growing data.
I chose to save the measurements in a stack-like structure. Every measurement is saved sequentially, with a new key indicating the measurement and a sequence number. Three infos have to be stored, the timestamp, the analog input number, and the temperature measurement.
Using a global counter, whose value is saved under a constant key, I can access to the last measurement, and all the ones before. The values expire after 24 hours.
Using memcache as the storage allows to divide the work between distinct programs, keeping them small and maintenable.
A single program is responsible to get the measurements from the arduino, compute the average temperature and store the value and timestamp in memcache. Only one instance of the storage program should be running.
A different program can then get the data from memcache and display it or use it for other purposes. There is no limit to the number of simultaneous programs for reading.
- tempstore: reads temperature readings from arduino and stores them to memcache
- tempread: prints last readings on the console
- templast: prints readings for the last 24 hours on the console
- tempgraphsaver: saves last day readings as a graph in a png file. Add labels to readings on command line.
- tempgraph: displays last readings as a graph in an interactive display
A few settings can be changed using environment variables:
- ARDUINO_USB_PORT: The usb device to use to communicate with the arduino
- ARDUINO_BAUD_RATE: The baud rate of the communication
- ARDUINO_NUMBER_OF_INPUTS: The number of analog inputs to track (1 to 5)
- GRAPHS_OUTPUT_DIRECTORY: a directory where to save the graphs
- MEMCACHED_HOST = ip:port of the memcached used to store the readings
- MEMCACHE_EXPIRATION_TIME: ttl in number of seconds of memcache entries
- SAMPLE_PERIOD: number of seconds to average before storing in memcache
- SAMPLE_WINDOW: number of readings to keep in the graph.
Requires memcached, python_memcached, pyserial and matplotlib.
tempgraph command requires matplotlib compiled with a tk backend.
git clone https://github.com/pchartrand/temperature-monitor.git
cd temperature-monitor
sudo pip install .
pip install .
pip uninstall temperature-monitor
A setup with 5 ntc for outside, basement, ground floor, second floor and attic temperatures.
The temperatures are displayed on a lcd display shield atop of the arduino.
The shield illuminates when it's bushbuttons are pressed and gives readings in Celsius, Farenheit, Volts or 0-1023 range.
The temperature readings are stored in a memcached instance of a raspberrypi housed in the same box.
The raspberrypy readings are accessible on the local network thru memcached using the same library.