Friday, January 02, 2015

A Sensorium

My first Arduino project is a "sensorium", for recording data from sensors, storing it and then later uploading it. The idea is that this is something you could carry around. It takes a reading such as temperature or UV periodically and save the readings in RAM. Pressing a button initiates a transfer over the serial line. The hardware is trivial: you can connect up a switch with just a pullup resistor, as in http://arduino.cc/en/Tutorial/Button, and connector a sensor such as the DHT11 temperature/humidity sensor as in https://learn.adafruit.com/dht/overview. During development I also found it helpful to hook up a LCD display. You can do debugging over the serial line, but if you are using the serial line for something else, another way of displaying what is going on is convenient. I used one of these: https://www.adafruit.com/products/181.

I've put the code online at http://www.friendlymoose.com/software.html. A line-by-line analysis would probably be boring, so here's a quick overview of the main elements. There is a template class, CircularBuffer, used to store the data and send it over the serial line. It keeps the data in a fixed size array. The parameter of the template indicates what kind of data to store. The buffer is circular, in the sense that once all the space has been used up, the oldest item will be overwritten. The serial protocol is quite simple. It sends one data item at a time, preceding by its size in bytes (one byte, and so a maximum of 255) and a one byte checksum. It then expects to get a one byte response code and the same checksum back. If it doesn't get an OK response, or if the checksum doesn't match up, or if it gets no response within a given time, it will try to resend the data a few times, and eventually give up. If the data does get through, it is removed from the buffer. I decided on sending only one data item at a time after some experiments with sending larger amounts of data. It turned out to be hard to get the timeout for the response right in a reliable way. There's a utility function to send several data items one by one.

There is a class DHTSensor for representing the data from the sensor. It also has methods for deciding if it is time to read the sensor and for reading it and storing the data in the circular buffer. In an ideal world, I would have written a base class here and subclassed it for the DHT11 sensor. Deciding on when to read the sensor is done by just looking at the millisecond timer and comparing it to when the last reading was taken. I have it set up for one reading per minute.

If I'm carrying this thing around, I want to run it from batteries, and that means saving power if possible. There is a base class PowerSaver with three variants, selected by a #define. One does nothing. One puts the Arduino into idle mode. It wakes up every millisecond. This is the one that I currently use. The final variant shuts down down the Arduino for 8 seconds at a time using a watchdog timer. The ideas here are based on http://www.gammon.com.au/power. The third version would give the best power saving, but it makes it hard to decided when the read the sensor, as the millisecond timer gets shut down as well. I'm not sure how well the power saving works in practice. Sometime I'll measure how long it takes to drain a fresh battery.

The button for initiating serial transfer fires an interrupt handler, which saves a boolean to be read by the main loop. Finally, the loop() function itself checks whether the button handler set the boolean and sends the data over the serial line if so. It also decides if it is time to read the sensor. At the end of the function, it puts the processor back into its low power state. The serial port is reopened each time the data is sent. This seems to work better when the serial line can be physically disconnected and reconnected.

The receiving end of the code, ardser, is written in Python and is also at http://www.friendlymoose.com/software.html. This is a Windows version, though only the port should need to be changed to work on Linux. It requires the pySerial library. I've had some difficulty making the connection work. The best sequence seems to be connect the USB cable, start ardser, then press the send button on the Arduino. The code outputs the triples of temperature (Fahrenheit), humidity (%) and time (seconds) sent from the Arduino. Here's an example, from my evening walk with the dog, with readings inside the house at the beginning and the very end.
 (6800, 4100, 60)
 (6800, 4100, 120)
 (7160, 4100, 180)
 (6440, 4100, 240)
 (5900, 4200, 300)
 (5720, 4400, 360)
 (5720, 4500, 420)
 (5720, 4500, 480)
 (5720, 4600, 540)
 (5720, 4700, 600)
 (5720, 4700, 660)
 (5720, 4700, 720)
 (6080, 4900, 780)
I doubt that this is very accurate. The temperature in my house is probably around 68F, and wunderground was reporting an outside temperature of around 57F, so this looks OK. I don't know what the humidity in the house is, and wunderground gives 60% outside, so it looks like the sensor is off, or there is a bug in the code that reads it.

More on the physical construction later.

Jan 10 2015: Updated Sensorium.ino with a couple of minor bug fixes.

No comments: