Last time we had a look at the sensor hardware, now it’s software time. I’m sorry, there’s less pretty pictures. It’s boring nerd stuff, that’s just the way it goes sometimes.
I’ll leave the Raspberry Pi setup as an exercise to the reader. There are many fine tutorials. Get a microSD card, flash Raspbian to it (I used the desktop version for easier setup then had it boot to CLI afterwards.) Obtain a USB wifi adapter (check compatibility) if you don’t have onboard wifi. Connect it to your network (or use a cable.) Please please please change the default password on the Pi. Enable SSH at the very least unless you want to sit wherever your Pi is while you’re monitoring it, which would sort of defeat the purpose.
So at this point we have the Mega2560 (again, another more reasonable board would work, a Pro Micro would be ideal probably) listening to the sensors, cleaning up the data a bit, and sending it to the Pi over the USB serial connection. You’ll need drivers for this if your board uses a CH340G, almost everything else works out of the box. Here’s what’s running on the Arduino board.
/* Ideally, spitting things over serial while using very little power. */ // include the library code: #include "DHT.h" // dht sensor's output #define DHTPIN 30 // dht sensor's "power" #define DHTPOWER 40 // either this or a DHT11 #define DHTTYPE DHT22 // flame sensor power is connected to 3.3V rail // only analog output is connected, digital trigger is not #define FLAMEPIN A0 // initialize the library with the numbers of the interface pins DHT dht(DHTPIN, DHTTYPE); void setup() { // totally powering the dht22 from a gpio pinMode(DHTPOWER, OUTPUT); digitalWrite(DHTPOWER, HIGH); // flame sensor input pinMode(FLAMEPIN, INPUT); // sure why not Serial.begin(9600); } void loop() { float h = dht.readHumidity(); float t = dht.readTemperature(); // 600-700+ seems to be a normal reading // under 400 and i'd be extremely worried // if it's under 100 the sensor's on fire probably int f = analogRead(FLAMEPIN); // read errors are extremely common on the first readings if (isnan(t) || isnan(h)) { } else { // output it to wherever Serial.print("T"); Serial.print(t); Serial.print("H"); Serial.print(h); Serial.print("F"); Serial.println(f); } delay(500); }
It appears the tabs don’t work. Thanks, WordPress. You get the idea. Basic sanity checking on the sensor readings, not much else, let the Pi deal with it. You’ll want to adapt it to your sensors obviously. I’m only using one analog sensor at the moment so I’m not too concerned with what AREF is set to or anything like that.
As a side note, I’m not powering the DHT22 from a digital pin just because I’m an idiot (although it’s possible), with the LCD shield in place there actually aren’t any exposed 5V pins on the Mega, but there’s a whole bunch of GPIOs. The sensor doesn’t use even close to enough power to stress the output pin.
The Arduino code is brief. This would run on almost literally any board you could buy. Currently communication is only in one direction although the code on the Pi anticipates that changing.
Before we move on to the Pi, a word of caution. The Pi uses 3.3V logic. Arduino boards vary. The Mega uses 5V logic. This is why I’m using the USB serial chipset and not one of the Mega’s several hardware serial ports. Also I’m saving one of those for a future datalogger. If your voltage levels are compatible and Linux drivers are being a pain for your CH340G you could totally use a hardware UART to communicate to the Pi.
Find where your Arduino shows up on the Pi (mine was on /dev/ttyACM0), you’ll need to know that. You shouldn’t really have to install anything that isn’t included in the basic Raspbian image.
This is the script that reads the serial data. Yeah, perl. Whatever, I like it.
#!/usr/bin/perl # reads serial output from the arduino (DHT22 sensor, flame sensor) # the thing is, jumping into the serial stream mid-write gives a lot of funky data. # attempts are made to filter it out # expected data format is 'Tnn.nnHnn.nnFnnn' use IPC::Open2; # we don't technically need open2 but it's easier and lets us talk both ways in the future open2 $out, $in, "/usr/bin/minicom -b 9600 -o -D /dev/ttyACM0" or die "Could not open serial portn"; # basically just read data until we get something properly formatted while(<$out>) { my $line = $_; # is the data even remotely valid? next unless $line =~ /^T/; chomp($line); $line =~ /T([1234567890.]+)H([1234567890.]+)F([1234567890]+)/; my $t = $1; my $h = $2; my $f = $3; # the below is largely for ignoring weird serial data next unless $t =~ /^\d{1,2}\.\d{1,2}$/; next if length($t) == 0; print("T$t"); print("H$h"); print("F$f\n"); # break out of the loop (and end the script) when valid data is received last; };
Oh hey, tabs again. Ah well, it’s short. You’ll work it out.
The most obvious thing is that it has the same output as the Pi, in terms of format. This time it’s just ‘guaranteed’ to be good and valid data in the appropriate format. This is what other scripts use to get sensor data, with the exception of the userland one, which I’ll get to.
This is the output.
Of course, that’s real handy if all you want to do is see what it’s like out in the shed. Which is exactly what I wanted it for, initially, because I was getting real sick of walking out there every 10 minutes while getting the heat dialled in.
The next script logs the output. It does so every minute. It logs to three files, all of which have been set to mode a+r, which will become important later. It also copies the logs to the web server root directory, where they sit not doing anything, because I haven’t gotten to that part yet. I’m using lighttpd because I don’t really want to mess with Apache for something this trivial. Since it only does this every minute, I’m not super concerned about it losing power during the fraction of a second it’s writing, etc.
#!/usr/bin/perl $delay = 60; # 1 minute $| = 1; while(1) { $data = `/usr/bin/readsensors`; # print $data; chomp $data; $data =~ /T([1234567890.]+)H([1234567890.]+)F([1234567890]+)/; $t = $1; $h = $2; $f = $3; open(TEMPLOG, ">>", "/logs/temperature.log"); open(HUMLOG, ">>", "/logs/humidity.log"); open(FLAMELOG, ">>", "/logs/flame.log"); print(TEMPLOG time . " " . "$t\n"); print(HUMLOG time . " " . "$h\n"); print(FLAMELOG time . " " . "$f\n"); close(TEMPLOG); close(HUMLOG); close(FLAMELOG); system("cp /logs/*.log /var/www/html/"); sleep($delay); }
Currently I’m starting this script by hand, but it should really be started immediately at boot time, ideally as a service. Also something I haven’t gotten to yet.
Everything’s timestamped, so if you wanted to feed this to RRDtool or something (possibly an upcoming blog post!) it’s in a semi-appropriate format, or at least you can write a simple script to make it so. Here’s what the log files look like, in this case the flame sensor.
root@shedpi:~# tail /logs/flame.log 1513753311 713 1513753371 714 1513753431 714 1513753492 712 1513753552 713 1513753612 713 1513753672 713 1513753732 712 1513753792 713 1513753853 713
Huzzah! Data! Everyone loves data! So now we can check the status of our sensors, read data from them, and log it to disk (to the tune of roughly 25MB/year with these sensors.) Presuming we’re running as root at the time. Which is… sort of really not great. We want any user to be able to see this data, but we don’t actually want to let any user access the serial port, or it could mess up the script trying to use it.
The last script is “sens”, because I wanted a short name. It doesn’t actually read the sensors, which it wouldn’t be able to as a normal user, but it DOES give you the last line of each log file. So your data could be up to a minute old, but it’s the most recently collected data by the logger. It’s an imperfect solution.
#!/bin/sh tail -n1 /logs/temperature.log tail -n1 /logs/humidity.log tail -n1 /logs/flame.log
…I didn’t say it was complicated. So now we can read sensor data, sort of, without having to be a superuser at the time. Its output looks like this. It could be a bit cleaner, but I can tell which number is which sensor pretty easily.
And that’s pretty much it, up until now. More construction-related projects are occupying my free time at the moment, and it does what I need it to at this very second, which is just read and log temperature and humidity.
For the more DIY-oriented among you, this is all you need, once you can get sensor data from a regular user account, you can do all sorts of things with it. Graph it, analyze it, e-mail it to yourself every 12 hours if you’re a nervous parent.
Likely additions this week include using a big-kid database instead of a flat file, archiving old data (do you really need to know how humid it was 119 days ago?), and setting some basic range alarms on the Pi to trigger… well, I’m not sure yet. Of course at this point I should probably advise everyone not to work with line voltage unless they’re experienced and comfortable with it, and not to leave any line-voltage-using DIY creation unattended unless you’re very confident in it.
For the less DIY-oriented, I’ll be doing some more actual useful things with the data in the coming days once I have a wee bit more time. I may even offer some pre-built units for sale! Stay tuned!
Hot tip: Does your Pi keep dropping the wifi connection after a while? Possible solution here! (Worked for me!)