Remote Sensing, Part 2

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.

Screen Shot 2017-12-19 at 2.02.44 PM.png

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.

Screen Shot 2017-12-19 at 2.02.10 PM.png

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!)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s