Garduino Phone – Saving Plants from Negligent Parents using Twilio, Arduino and Sinatra

June 18, 2014
Written by

phone-case

A few months back I built some raised beds with the intention of giving my wife and I something to do on the weekends. However two things happened immediately after I built the beds that really hindered my gardening ambitions. First, the garden went totally insane sprouting up 4 foot brussel sprouts, producing more fat beets than Ryan Lewis and in general taking over our whole backyard. Second and more importantly, we had our first child. Needless to say the garden quickly became neglected. Our rock-hard broccoli florets and orange wilting lettuce were obviously under-watered plants. I was letting my garden down even though my friends took to calling me “Farmer Jarod.” It was time to hack this garden and show my plants some love.

The Garduino Phone

My first approach was to scour the internet for an out-of-the-box tutorial. However, I found that most of the solutions out there were either too basic (one sensor/one-way communication), or way over the top (massive clusters of micro-controllers). In the end I wanted a little brain in my garden that would alert me when things got out of hand, as well as respond to my inquiries when I’m out of town and need to check the health of things. So I decided to build this little “Garduino Phone” and put it inside of an old rotary telephone that I bought for $3 at a yard sale.

Here’s what you need to understand before you build a garden monitor. In San Diego we get frosty nights and blistering summer days which can kill my cold crops (brussel sprouts, cabbage, etc). Even warm crops like corn and onion don’t like soil that ever gets above 90 degrees, and this is a real possibility in San Diego. So our garden needed to have a moisture sensor and a thermo sensor, but if you are building this for indoor use you could probably skip the thermo sensor. Also if you are building an indoor garden you should check out this post on a self-watering plant using Arduino.

My ideal garden phone would work like so:

Picture of how twilio connects to garden monitor.

Step 1: Setup

Prepare Your System

  1. Download and install the Arduino IDE from http://arduino.cc/en/Main/Software.
  2. Grab the code from github https://github.com/jarodreyes/garden-phone/
  3. Install the ngrok utility from https://ngrok.com/download
  4. Signup for a free Pusher account http://pusher.com/pricing

Signup for Twilio
Go to twilio.com/try-twilio and sign up for your free number. As good measure whenever I buy a new phone number I immediately add it to my ENV variables.

Step 2: The Circuit

Arduino circuit with breadboard.
Arduino circuit with breadboard.

Setting up the circuit should be simple but I found some gotchas that I’ll be pointing out along the way so that you don’t get stuck. We have two sensors, one in a digital pin and the other in an Analog pin. The only real trickery we do here is add a 4.7k pull-up resistor in order to use the 1-wire bus. Here is the circuit in completion.

Arduino circuit Diagram
I find it easier to just follow the diagram.

Instructions:

1. Plug WiFi shield into Arduino Uno
2. Jumper from 5V to (+) on breadboard
3. Jumper from GND to (-) on breadboard
4. Plug in moisture sensor to (-) (+) and A0 on Arduino
5. Thermo sensor (blue/green) wire to Digital Pin 3
6. Thermo sensor (white/yellow) wire to […check diagram]
7. 4.7k resistor from the green wire to power

Step 3: Server Code

I chose to use Sinatra as our Server. Sinatra allows us to write a simple one-page Ruby file that runs the whole shebang. In this case our server only needs to do three things:
Receive data from the arduino
Send an SMS to a phone
Receive SMS from my phone, send data from Arduino in return (using Pusher and Twilio APIs)

First let’s setup our Sinatra file with all of the requirements. Github ➭

require 'sinatra'
require 'twilio-ruby'
require 'pusher'

before do
  # Setup Pusher API 
  Pusher.app_id = “[your pusher id]”
  Pusher.key = “[your pusher key]”
  Pusher.secret = “[your pusher secret]”

  # Setup Twilio API
  @client = Twilio::REST::Client.new “[your twilio SID]”, “[your twilio token]”

  @twilio_number = “[your twilio number]”
  @my_cellphone = ‘5558675309’
end

We setup our app with requirements, initiate the APIs with their tokens and define some global variables. Next let’s setup our endpoint that will listen for arduino data. Github

get '/arduino-data?*' do
  # Params sent by Arduino
  alert = params[:alert]
  temp = params[:temp]
  moisture = params[:moisture]

  # Check if this is an emergency or just a status check?
  if alert
    if moisture
      body = "Garden Alert: #{alert}. Water level: #{moisture}."
    else
      body = "Garden Alert: #{alert}. Temperature: #{temp}."
    end
  else
    body = "Garden Moisture: #{moisture}. Garden Temp: #{temp}."
  end

  # Send SMS 
  message = @client.account.messages.create(
    :from => @twilio_number,
    :to => @my_cellphone,
    :body => body
  )
  puts message.to
end

This function grabs the parameters from the GET request, checks if it’s an alert and then sends the SMS. The reason we are checking if this is an alert is because the Arduino is capable of initiating this request when it sees abnormal sensor data, in which case it will pass an alert parameter which acts as a warning message (eg: “Too Hot!”; “Low Water!”)

Lastly let’s setup our endpoint for Twilio. Github

get '/check-status?*' do
  puts Pusher['garduino'].trigger('status', {:message => ''})
end

We’ll talk more about this little guy later, but for now just know that it serves up the URL we are going to give Twilio.

Now we just need to host this Sinatra app so that our Arduino has somewhere to post it’s data. Luckily Ngrok makes this incredibly easy.

Hosting with Ngrok
In a new terminal window run your Sinatra app using rackup:

cd ~/Garden-Phone-folder
rackup

Now you should have a Ruby instance running on port 9292. So in order to make this a public url simply run ./nrgok 9292  from the directory you installed it.

Now let’s add this URL to Twilio. Once you’ve logged in to Twilio, click on numbers and choose the number for your garden monitor and you should see the following screen:

number-setup

Enter the Ngrok url into the Messaging url field and you’re done! Now that we have a public endpoint let’s setup the Arduino.

Step 4: Arduino Code

When I struck out on this project I was slightly daunted by the lack of consensus and documentation about using web sockets to interact with an Arduino. However, it turns out the most straightforward approach is to use the Arduino’s WiFi client to initiate HTTP requests.

The sketch I included in the github repo contains a few lines of code that initiates a Pusher connection and receive an inbound command, but since this process has been thoroughly covered in my Halloween Arduino post I will skip it. If you would like more information on how to enable your Arduino to accept commands using the Pusher library read my Halloween post. Now let’s talk about the code.

The Arduino code needs to do the following things:

  1. Connect to the wireless access point (WPA in this example)
  2. Assign the two sensor inputs and register the sensors
  3. Get sensor readings
  4. Send an alert to my server when the sensors detect dangerous levels

Before we write the code you need to go grab these libraries and install them in your Arduino libraries folder:

  • ArduinoPusherLibrary – Allows the Arduino to communicate with the Pusher API
  • Timer – Timer library to replace the clunky delay() functions
  • DallasTemperature – Reads & converts temperature readings from temp sensors
  • OneWire – Allows arduino to communicate with multiple sensors on one data line

Alright let’s dive in to the code. You can grab all of the code from github, including a version of the file called garduino-debug.ino that has a bunch of Serial outputs to help you debug issues. First let’s setup the file with the libraries and pointers we need to make things work.

#include <Timer.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <WiFi.h>
#include <PusherClient.h>
#include <stdlib.h>

Timer timer;
PusherClient client;
WiFiClient wifi;

#define ONE_WIRE_BUS 3
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress insideThermometer;

int MOISTURE_SENSOR = 0; // Pin for the Moisture Sensor
int MAX_TEMP = 85;
int MIN_TEMP = 40;
int MIN_WATER = 300;
int MAX_WATER = 700;
int status = WL_IDLE_STATUS;     // the Wifi radio's status
int moisture; // sensor reading for moisture. 0 - 950

String alertMsg, alertBody; // Buffers for alert messages.

char ssid[] = "ECTO-1";     //  your network SSID (name) 
char pass[] = "WhoUgonnaCall?";    // your network password
char server[] = "[your ngrok url]"; // Enter the url you get when running ngrok
char PUSHER_KEY[]= "[your pusher api key]";
char PUSHER_CHANNEL[]= "garduino";
char tempStr[6]; // buffer for temp incl. decimal point & possible minus sign
char waterLvl[6];

Before we move on be sure to replace the ssid[], pass[], and server[] pointers with your own.

Instead of starting with the typical setup() function let’s write some utility functions that we can then call later on from setup(). The first function we’ll write connects to a WiFi network and Pusher.

void setupWifiPusher() {
  status = WiFi.begin(ssid, pass);
  if ( status != WL_CONNECTED) { 
    while(true);
  } 
  else {
    if(client.connect(PUSHER_KEY)) {
      client.subscribe(PUSHER_CHANNEL);
      Serial.print("Looks like we connected");
    }
    else {
      while(1) {
        Serial.print("Did not connect");
      }
    }
  }
}

First we initiate a WiFi connection. Then if we successfully connect we initiate a Pusher client that will be able to listen to the “garduino” channel. Otherwise we print an error “Did not connect” to the Serial port.

Now let’s write the functions that will get our sensor readings from the garden.

void checkTemperature(DeviceAddress deviceAddress)
{
  float temp = sensors.getTempF(deviceAddress);
  dtostrf(temp, 6, 2, tempStr); // Min. 6 chars wide incl. decimal point, 2 digits right of decimal
  if (temp > MAX_TEMP) {
    alertBody = "alert=too_hot&temp=";
  } else if (temp < MIN_TEMP) {
    alertBody = "alert=too_cold&temp=";
  }
  alertMsg = alertBody + tempStr;
  sendStatus(alertMsg);
}

void checkMoisture()
{
  moisture = analogRead(MOISTURE_SENSOR);
  if (moisture < MIN_WATER) {
    alertBody = "alert=water_low&moisture=";
  }
  alertMsg = alertBody + moisture;
  sendStatus(alertMsg);
}

void checkSensors() {
  checkTemperature(insideThermometer); 
  checkMoisture();
}

Now things are getting interesting. In this chunk of code we have two functions that check each sensor reading and then send alerts if the readings are abnormal. Lastly we have a wrapper function that calls both of the sensor functions– this will act as our callback later. As of now none of these functions would work since we haven’t written the sendStatus() function that will send an alert to our server. Let’s write that function now.

void sendStatus(String data) {
  if (wifi.connect(server, 80)) {
    wifi.println("GET /arduino-data?"+ data +" HTTP/1.1");
    wifi.println(‘[your ngrok url]’); // replace with your ngrok URL
    wifi.println("Connection: close");
    wifi.println();
  }
}

This function accepts a string, and then makes a GET request to the endpoint that we wrote earlier in our Sinatra app. Now we have all the pieces in place to send an alert, but we need to bring it all together in our setup() function.

void setup(void)
{
  Serial.begin(9600);
  setupWifiPusher();
  sensors.begin();
  if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0"); 
  sensors.setResolution(insideThermometer, 9);
  timer.every(10 * 60 * 1000, checkSensors); // 10 minutes
}

void loop(void)
{ 
  monitorPusher();
  timer.update();
}

This should be familiar to you if you’ve written code for Arduino before. In our setup function we initiate our Serial output, call our Wifi function and then call our checkSensors function. You will notice we are calling checkSensors as a callback to a timer call. This is because the end result of our sensor functions is to send a text message and we don’t want to fire off 1000 texts as soon as there is a bad reading. This timer call acts as a throttle that will only check the sensors once every 10 minutes.

In our loop function we check Pusher for any changes then call timer.update which maintains the state of our Timer. Of course, now you’ll notice we haven’t written the function that monitors Pusher, but since this is thoroughly covered in my Halloween post you can just checkout the code on github.

Now load the whole sketch on to your Arduino and put it in the garden!

Final Step: Put it in the garden

phone-case

For this last step feel free to have fun. All you need to remember is that you need to protect the electronics from the environment. In my case I found an old rotary telephone and used that to house the electronics.

Of course if the phone casing isn’t realistic you can always use a 2-liter bottle or anything that can be made to be waterproof. I hope that you have fun with this project. Be sure to hit me up on email or twitter if you have any questions or just want to chat about the inevitable enslavement of humans by giant robo-garden-phones. Happy hacking!