Control your field's irrigation system with your mobile

A few weeks ago, I was showing my dad my latest project, the Arduino RF remote control, and he lamented the fact that I still hadn’t made an irrigation system remote control for him. We have a few acres of fields that need irrigation, which is done with a system of pipes from a central water pump. At that point, however, the fact that he needed a remote control for the pump was as news to me as it is to you, so I asked him what he needed exactly.

He told me he needed a system that would allow him to call or text a phone number and turn the pump on and off remotely, as well as something that would text him if there was a problem that turned the pump off prematurely.

An irrigation spoolAn irrigation spool

Irrigation here is done with large spools connected to the pumps, but there may be problems like loss of power, pipes not connecting properly and opening up, losing pressure, etc. Since this can throw a farmer’s schedule out of whack, a system that will notify you immediately and save you from coming back to the field twelve hours later only to find that you watered five minutes’ worth of field before a pipe broke is invaluable.

Arduino to the rescue

The Arduino is perfect for a project like this. It’s low cost, only requires a single source of power, doesn’t have an OS to freeze/hang, and has many standard peripherals. That sounded ideal for what I wanted to make, so I scoured the internet for an Arduino GSM shield. In the end, I found a shield and library for about $30, which is dirt cheap. The complete system cost about $50, as opposed to the $700 most other systems cost.

Luckily, GSMlib is very easy to work with, and, as soon as I found out which pins I could use and which were off limits, I was off to the races. As a short example, to send an SMS when an authenticated phone number (a number that is saved to the SIM card) calls you, all you need to do is:

stat = call.CallStatusWithAuth(number, 1, 3);
if(stat == CALL_INCOM_VOICE_AUTH) {
    call.HangUp();
    sms.SendSMS(number, value_str);
}

Describing the system

The actual pump control system is rather simple. There is a way to turn it on or off, which is either a single relay which opens and closes, or a two-relay system that emulates on/off push-buttons. I wanted to be able to support both, which wasn’t really very difficult.

Apart from our output, there are also inputs. There is a relay that opens and closes when the system is up and running, and three relays that open when there is an error (power loss, pressure loss, high temperature). Our software generally needs to send you an SMS when the system starts and stops running (for any reason, whether you started it by calling or not), and an SMS with the specific error reason when one of the error relays gets triggered.

We also wanted a way to disable SMS sending, to make sure you don’t get needlessly billed if you start/stop the system manually while you’re at the pump itself (you obviously don’t want to get texts that pressure has been lost when you’re standing next to the pump). That’s achieved by a straightforward switch input, turning it on enables SMS sending and turning it off disables it.

There were some minor considerations, such as error relays that override others (when you lose power, all the other error relays are going to open as well, so you only need to send a “Power has been lost” message), and error conditions specific to the error (e.g. shutting the system off makes the “loss of pressure” error trigger), but it’s pretty straightforward.

Implementing the error texts

The function that handles this is only a few lines long. Initially, we check to see if a relay state changed (we want to be edge-triggered to avoid spurious loops when an error relay stays activated, for example):

for (char i=0; i < INPUT_LENGTH; i++) {
    pinState = digitalRead(inputPins[i]);
    if (pinState != pinStates[i]) {
        // If the pin's state changed, note it.
        statesChanged[i] = 1;
    }
    // Update the state.
    pinStates[i] = pinState;
}

After we have all the state changes and new states, deciding what to do is very easy:

// Decide what happens.
if (statesChanged[0] == 1 && pinStates[0] == OFF) {
    // Phase dropped, send message regardless.
    sendSMS("Phase dropped.");
} else if (statesChanged[1] == 1 && pinStates[1] == OFF) {
    sendSMS("Thermal dropped.");
} else if ((statesChanged[2] == 1 && pinStates[2] == OFF) &&
           (statesChanged[3] == 1 &&  pinStates[3] == OFF)) {
    // Pressure dropped *and operation stopped simultaneously*.
    // This is because we don't want to be notified of normal
    // pressure drops after we stop the system ourselves.
    sendSMS("Pressure dropped.");
} else if (statesChanged[3] == 1 && pinStates[3] == OFF) {
    sendSMS("Pump shut down.");
} else if (statesChanged[3] == 1 && pinStates[3] == ON) {
    sendSMS("Pump started.");
}

Wrapping it up

The finished system should be pretty plug-and-play. If you want to use it, and have mostly-compatible error relays with mine, you can just install it onto your Arduino and run it.

You can find the complete code for it in my GitHub repo:

https://github.com/skorokithakis/arduino-irrigation

Pull requests for functionality are welcome, and so are success stories. Failure stories are also welcome, but they make me less happy. I will also be expecting your comments below, or you can Tweet at me. I hope Arduino-irrigation will be at least a bit useful to you, thanks for reading!