Secrets of Conductor Operations: Using A Microcontroller To Keep Your Plants Alive
On the operations team at Conductor, automation is our watchword. Most of the time, the systems we automate are made of software, but this post is about a project I worked on to automate a task in the physical world, and describes how I was able to use our centralized monitoring console to publish and alert on the status of that task.
The story begins in October of this year, when Emilie, one of my fellow operations engineers, left Conductor’s home base in NYC to move to Texas. We use technology to keep her in the loop as part of the team: Daily Skype for voice chat (and to put her head on one of our giant LCD televisions like The Wizard of Oz), Openfire XMPP chat for everything else. But she’s got a physical avatar as well: the Peace Lily plant she left behind on her desk, which we now have the pleasure of watering. So, nature is cool and whatever, but taking care of that plant proved to be strangely difficult for me and my team. A simple solution, like having the on-call engineer water the plant in addition to emptying the support queue, was our first thought. (Fixing tech support issues and watering plants sounds like a pretty ideal week if you ask me…) However, we found that the person on call — and the rest of the team — had enough work to do that we often forgot to walk over to check the moisture level of the plant. It became clear that without a way to grab our attention, the plant was going to die a slow and parched death.
We realized that this problem would be a great candidate for automated alerting. After some quick research and a mouthful of “apology cookies” baked for us by the last developer to break the build, I felt sure that we could put together an Arduino-based tool to remind us to take care of the plant. The Arduino is a programmable, open-source microcontroller that’s designed to make it easy to build machines that perform simple tasks. When you combine it with one of many compatible sensors and peripheral devices, it becomes a hub for integrating that hardware with whatever software systems you can code up. I first got familiar with the Arduino after I adopted two young cats and quickly realized I didn’t have the time to play with them as much as I’d like. (Read on to see how the Arduino helped me keep them entertained…)
To keep the plant watered, I figured we would need a moisture sensor that we could stick in the dirt, and an LED to let us know the moisture level: Off when the plant was happy, on when the plant needed a drink or two. The Arduino would act as the bridge between the sensor and the LED, and I’d write some code to control the LED based on data from the sensor. I found a moisture sensor via a quick search on Amazon. If you’re playing along at home, we bought the Wrobot model, but any of the ones that come up in the results should do just fine.
Assembly was a piece of cake, or perhaps some sort of delicious pie. Using male/female jumper cables, hooking everything up just meant plugging it all into the Arduino. There are three connectors on the moisture sensor (positive, negative, and action), and two for the LED (positive and negative). For the moisture sensor, I hooked up the positive connector to the Arduino’s 5V input, the negative to the GND input (right next to the 5V), and the action to the Arduino’s A0 slot. I found two types of LEDs when I was shopping around. I bought the kind that’s just the bulb with two wires sticking out of it, and hooked up the longer one to port 12 on the Arduino, the shorter one to GND. (If you have the type that is like a little chip with a few connector pins, just connect positive to port 12, and negative to GND.)
The C code to drive our new plant-watering monitor was also quite simple and concise. I wrote everything in the Arduino IDE, which takes care of compiling everything to the bytecode format that the Arduino understands, as well as uploading it to the board. Let’s dive in:
int led = 12; int moistureSensor = 0; void setup() { Serial.begin(9600); pinMode(led, OUTPUT); } void loop() { if (analogRead(moistureSensor) < 300) { digitalWrite(led, HIGH); } else { digitalWrite(led, LOW); } delay(200); }
That’s everything you need for this neat little piece of awesome. The setup function is the first thing that’s going to get called when your Arduino starts up. It’s where you put, well, setup code. The loop method gets called after setup is complete, and it keeps getting called, over and over again, as long as the device is powered on. Let’s go over this line by line:
* Line 1 and 2 simply defines which port the positive end of the LED and moisture sensor is attached.
* Lines 4–7 open serial communications at 9600 bits of data per second, and instantiate the LED, respectively.
* In our main loop, we read input from the moisture sensor and turn the LED on or off.
We hooked the moisture sensor up to the zero pin on the Arduino’s analog channel, so we’ll use the analogRead function to read a value from it. If that value is less than 300, we turn on the LED by setting the voltage on the Arduino’s twelfth digital pin to HIGH, and if not, we turn it off by setting it to LOW. That’s it! Our plant-quenchingly fantastic device is done. If the LED is on, water the plant, and if it’s off, party down.
So far, so good. But this solution has some limitations — we only know if the plant needs water if we’re in the office and within visual range. If the plant gets dry over the weekend, or the engineer whose desk it sits on (mine) goes on vacation for a week, no one will know how thirsty it’s getting. Over the years, we’ve learned that the key to monitoring our systems is making alerts come to you. If you have to go and actively discover whether your systems are healthy, you’ll never be able to scale up the number of machines and processes you have to manage. (And you’ll never get any sleep!) To step up our monitoring game, I decided to get Emilie’s plant hooked up to the Interwebs — specifically, to Incinga, our internal alerting system. We use Icinga for monitoring our entire ecosystem, from simple things such as whether a database server is up and running, to reporting on more complex status, like making sure our ETL processes haven’t fallen too far behind the flow of incoming data. Icinga integrates seamlessly with a service called PagerDuty, which we use as an app (for both Android and iPhone), and which sends SMS alerts to the on-call operations engineer (both primary and secondary, escalating if alerts don’t get acknowledged — it’s very fancy). Knowing that we’ll get a text if and only if something’s wrong with our application helps us make sure Searchlight is always at its best, and also lets us spend more quality time with our families. Icinga and PagerDuty are where we look when we want to know what needs our attention, and so that’s where we’d need to see Emilie’s Peace Lily show up if it was it need of precious, precious water.
Icinga works the same way Nagios does, by running periodic checks that you register as plugins. These checks most frequently take the form of shell scripts that login or make requests to a remote machine in order to gauge the health of a process. To get Icinga to report on the health of the plant, we’d need our monitor to be able to respond to requests from our Icinga server, so we ordered an Arduino Ethernet Shield, which allows the Arduino to receive incoming TCP and UDP communications.
Let’s go over the additional code we needed to write to make our monitor aware of the network. First, we need to include the Ethernet library that comes with the Ethernet Shield and define our IP and MAC addresses:
#include <Ethernet.h> int led = 12; byte mac[] = { 0x11, 0x22, 0xDA, 0x0A, 0xCB, 0x60 }; IPAddress ip(192,168,2,1); EthernetServer server(80);
The MAC address can be found printed on the Ethernet Shield. Next, we need to start a web server and write content to it. That might sound hard to implement on such simple hardware, but it’s actually pretty easy!
void sendToIcinga() { EthernetClient client = server.available(); if (client) { char c = client.read(); Serial.write(c); client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println("Connection: close"); client.println(); client.println(""); client.println(""); client.print(analogRead(0)); client.println(""); delay(5); client.stop(); } }
This is a long function, but all it does is accept a new connection from the Ethernet server we defined and send it an HTTP response line and some simple headers, followed by a reading from the moisture sensor — just enough to expose a simple web service that can be queried by Icinga. After writing the response, we delay for 5 seconds so the client has time to receive the reponse packets before we close the connection. That’s it! Hard part over. Now we just need to add the sendToIcinga() method to our main method:
void loop() { if (analogRead(0) < 300) { digitalWrite(led, HIGH); } else { digitalWrite(led, LOW); } sendToIcinga(); delay(200); }
All we have left to do now is write a little shell script to alert, warn, or applaud us based on the response from our web service. Like Nagios, Icinga uses the exit code of the plugin execution to determine the status of the monitor, so our script needs to behave accordingly. “0″ means healthy, “1″ means warning, and “2″ means critical — sirens, flashing lights, etc.
function die { echo "CRITICAL: $@"; exit 2; } # exit critical function success { echo "OK: $@"; exit 0; } # exit success function warn { echo "WARN: $@"; exit 1; } # exit warn results=$(curl -s -g -m 15 "192.168.2.1" | sed 's/]*>//g') if [[ $results -lt 300 ]]; then die "EMILIE'S PLANT IS DYING" elif [[ $results -gt 300 && $results -lt 400 ]]; then warn "Emilie's plant will need to be watered soon" else success "Emilie's plant is super happy" fi
How cool is that?! Seriously — we just built a web service, a monitoring client, and integrated everything with Icinga in about 30 lines of code. This is what I’m talking about when I say that Icinga is easy. A simple bash script that uses curl to make requests to the server we set up is all we need to find out the status of the plant. If the plant gets too dry, we get text messages and emails telling us so.
Of course, as soon as we finished the project, we realized there were a lot of enhancements we wanted to make. First off, like any good engineer, we wanted the plant to post to Twitter. That turned out to be pretty simple, too. Check it out: Here we define a tweet method that uses the Twitter library from the Arduino Playground, and add in some messaging.
void tweet(char msg[]) { Serial.println("connecting ..."); if (twitter.post(msg)) { int status = twitter.wait(&Serial); if (status == 200) { Serial.println("OK."); } else { Serial.print("failed : code "); Serial.println(status); } } else { Serial.println("connection failed."); } }
All the printing we’re doing in this function is for informational purposes — the function logs things that are nice to know (or necessary to know, if we’re debugging). The really important bits are the calls to the Twitter API on lines 3 and 4. Call this function with your desired Twitter message payload, and it’ll Tweet like you’ve never Tweeted before. Here’s what the main loop looks like once we’ve integrated it with our tweet function.
if (analogRead(0) > 300) { digitalWrite(led, LOW); tweet("Emilie's plant is doing great!"); delay(300000); }
Of course, we couldn’t resist adding a bit of personality to the plant’s social media presence by giving it several different ways of phrasing its current status.
Even plants have feelings…
I am happy to report that the plant hasn’t died — at least, not yet — and the whole project was fun and easy.
There are tons of cool projects to do with Arduino, The Little Device That Could. Each of the projects above cost about $50, and they only took a handful of after-work hours to get up and running. On top of that, building gadgets like these is a cool way to impress your co-workers, significant others, cats, or even Batman (dude loves gadgets). I hope you’ve enjoyed this blog post, and I hope it inspires you to try out some Arduino projects of your own. Thanks for reading! And since you’ve made it this far, here’s a video clip of the device that got me into Arduino development in the first place, a rotating laser-pointer turret that I built to amuse my cats, Chaos and Catastrophe. (I’ll save the details of that project for another post…)