Christmas decorations are going up around the house, which means it's time for me to regularly forget to keep the Christmas tree watered. I've tried every kind of reminder/nagging app there is, but I just keep on putting off adding water to the tree, figuring that's there's still plenty of water in there.

This year, I'm trying something different, though.My 12 year old son and I built a gizmo that will send a push notification to my phone, and make my Sonos speak a warning (that's kind of my thing, after all), if the water level in the tree gets too low.

I had originally planned on using a Tessel, because they are so, so cool, and I love writing node code. But I got a Spark Core from a session in the IoT Zone at Dreamforce 14, and decided that this project was a great opportunity to give it a spin.

Our first plan was to use one of the digital GPIO pins on the Core to sense when a pulled-up input switched from low, grounded through water, to high, when the leads from my wires got out of the water because of the low water level.

spark_water_digital

This didn't work, though, as it turns out the resistance through plain old tap water is too high to bring the digital input low enough to read as LOW. Basically, the device read “open circuit, empty water well!!!” all the time and pinged my phone relentlessly.

So plan B was to scrap the digital input, and go with an analog input with an external pull-up to 3.3V. (I chose 220kOhm for the pull-up because that's all I could find on my workbench. I probably would have used something like 10kOhm if I could have found one.)

spark_water_analog

Using this setup, we noted that the analog input read about 3000 or so when the circuit was open (water below the two wires), and about 1000 when it was closed (both wires in the water). So using this information, the following wiring code was all we needed to send pushes to my phone.

#include "HttpClient/HttpClient.h"

unsigned int intervalCounter = 0;
HttpClient http;
int WATERSENSE = A7;

http_header_t headers[] = {
    { "Content-Type", "application/x-www-form-urlencoded" },
    { "Accept" , "application/json" },
    { "Accept" , "*/*"},
    { NULL, NULL } // NOTE: Always terminate headers will NULL
};

http_request_t request;
http_response_t response;

void setup() {
    Serial.begin(9600);
}

void loop() {
    if (intervalCounter > millis()) {
        return;
    }

    Serial.println();
    Serial.println(“Sensing water level.”);
    
    if (analogRead(WATERSENSE) > 1800) {
        request.hostname = "api.pushover.net";
        request.port = 80;
        request.path = "/1/messages.json";

        request.body = "token=PUSHOVERAPPTOKEN&user=PUSHOVERUSERTOKEN&message=Water+is+low";

        http.post(request, response, headers);

        Serial.print("Response from pushover: ");
        Serial.println(response.body);
        
        request.ip = (192,168,0,100);
        request.port = 5005;
        request.path = "/family room/say/The Water is Low";

        http.get(request,response);

        intervalCounter = millis() + 3600000;


    }
    
    else {
        Serial.print(“Still wet“);
        intervalCounter = millis() + 600000;
    }


}

In lines 30-42, we use an app I set up at Pushover to send a notification to my phone. Pushover is pretty cool: you install an app on your phone, register on their site, and use supplied tokens, along with an API endpoint, to send the notifications.

Finally, you can see where we sent some text for my Sonos to speak, using the great node-sonos-http-api library.

My 5 year old son wanted to get in on the “leet hacking” (as my older two facetiously call it) and built a fancy lego case for our device.

IMG_0299

This was a super-simple project, but served as a fun introduction to the Spark hardware. I'm really looking forward to digging in to their cloud offerings some more. I'm also pretty excited about keeping most of the needles on our tree through Christmas!