Pondering

After writing the pseudo code for the watering program, I let it simmer for a few days and then started on real code.  First thing I did was write a config file in the json format.  The target was to describe the watering zones in the system. ow many there are, should the program use syslog to log actions, and finally is there a rain moisture gauge attached and if so what kind is it (for now I only tackled a digital gauge that is either open or closed on GPIO 15), and there is a version number so I know what the file might contain as it evolves.  And yes the zones are 0 based, this file is not really meant for human consumption, though its pretty easy to read really.

Config.json

It looks something like this:

{
    "version": 0.1,
    "controller_name": "this watering system name",
    "log": 1,
    "zones": {
    "total": 8,
    "zones": [
       {
           "zone": 0,
           "name": "Human name of zone 0"
       },
       {
           "zone": 1,
           "name": "z1"
       },
       {
           "zone": 2,
           "name": "z2"
       },
       {
           "zone": 3,
           "name": "z3"
       },
       {
           "zone": 4,
           "name": "z4"
       },
       {
           "zone": 5,
           "name": "z5"
       },
       {
           "zone": 6,
           "name": "z6"
       },
       {
           "zone": 7,
           "name": "z7"
       }
      ]
    },
    "gauges": {
        "rain_moisture": {
            "present": 1,
            "bus": "GPIO",
            "type": "D",
            "device": 15,
            "signal": "NO",
            "use": 1
        },
    }
}

Watering

Once that was done and tested I located an open source json parser library that could parse that in easily.  No point in reinventing a wheel when there are perfectly good ones available.  The parser I ended up choosing was parson.  It’s lightweight and pretty easy to use. You can find it here and it uses the The MIT License (MIT) so all good open source stuff. Next I needed a watering program stored in json format.  So it contains the human name of the watering program, an option to use or not use the rain moisture gauge, then the zone info, including the count and zone info including zone number, how much water to deliver, the amount to deliver at one time, and a version number so we can track the file as it evolves.

wp1.json

{
    "version": 0.1,
    "program_name": "3 second watering",
    "use_rain_moisture": 1,
    "zone_info": {
        "total": 8,
        "WaterCycleExtraDelay": 30,
        "zones": [
            {
                "zone": 0,
                "WaterLength": 3,
                "WaterCycle": 2,
            },
            {
                 "zone": 1,
                 "WaterLength": 3,
                 "WaterCycle": 2,
           },
           {
                  "zone": 2,
                  "WaterLength": 3,
                  "WaterCycle": 1,
           },
           {
                  "zone": 3,
                  "WaterLength": 3,
                  "WaterCycle": 2,
           },
           {
                  "zone": 4,
                  "WaterLength": 3,
                  "WaterCycle": 2,
           },
           {
                  "zone": 5,
                  "WaterLength": 3,
                  "WaterCycle": 2,
           },
           {
                  "zone": 6,
                  "WaterLength": 3,
                  "WaterCycle": 2,
           },
           {
                  "zone": 7,
                  "WaterLength": 3,
                  "WaterCycle": 1,
           }
        ]
    }
}

Got that done, used the json parser to read that into the application.  Now we are cooking.  That sets up 8 zones to be watered 3 seconds each (that makes it easy to test) with a cycle time of 1 to 2 seconds to each zone will get run at least twice and 2 of them will run for a third time. So then I wrote the main code that will actually run the watering program and it’s working.  It’s not final, it has some bugs to be fixed but it works….

What remains to be done?

What remains before I can start testing it with some alpha/beta testers?  Some code cleanup add minor features, really it’s pretty much ready, it reads a rain sensor now (NO  or NC) it waters according to cycle, it knows time and date.

OK, sat down over the weekend and did a bit of pseudo code for the watering program.

Easy when you think of it:

  1. Grab command line to see what watering schedule to run
  2. Load system config so I understand my hardware
  3. Load the designated watering schedule
  4. Set up a loop to run all of the zones, if zone is not active or does not have any more water time go, loop to next zone, else:
    1. Check the global gauges to see if we can water, if not exit program with reason
    2. Check zone gauges to see if the zone can be watered, if the zone gauges say no water, zero the zone and go back to the top of the loop
    3. Determine how long to water, either to the limit of the soil or for the full zone time
    4. Start watering the zone
    5. Sleep for the determined time
    6. Stop watering the zone
    7. Go back to the top of the loop and keep doing it until all water delivered to all zones or we are bumped by gauges.
  5. Shutdown any specific hardware as necessary (make sure the water and the pump (if you have one) are off.)
  6. Exit program with status

Now there are some nuances there, logging and other bits but really that is it.  Overly simplistic and lots of detail to pay attention to but at the highest level no need to check things second by second.  And if for some reason I determine I have to check things more frequently than at each zone change I can by using threading but I don’t currently see the need for it.

So the first thing I decided to have a look at is what format are my data files?  Gave that a lot of thought, XML, YAML, JSON or some home cooked format.  After careful consideration of both the input web program and the watering program I decided on JSON for a bunch of reasons, its light weight, there are standard validation tools that can easily tell me if the file if formatted correctly, and its easy to write a json file from web software and their are a bunch of parsers under open source licenses so I don’t need to reinvent the wheel.  Finally it’s pretty easy to hand craft a json file and get it correct so I can get the watering program done and tested without the web front end.

In some ways I don’t love JSON, the pure form does not support comments in the file.  Several parsers do support C and C++ style comments but its not part of the standard as best I can tell.  Of course some people don’t believe in comments so it matters not to them but I do like them..  Oh well the rest of the file format I like well enough.

The other thing I thought about was possible devices attached to the sprinkler system to determine if I should actually water now, or things on the Internet I can reach out to, they are:

  1. Weather report
  2. Temperature gauge (don’t water if too cold)
  3. Rain moisture gauge (don’t water if raining right now)
  4. Rain metering gauge (don’t water if we got enough water in the last day, tricker as we have to have something monitor this all the time)
  5. Wind gauge (don’t water if the wind is blowing too hard, you will just water the neighbors yard)
  6. Ground moisture gauge (one for the entire yard or one per zone, if the ground has enough water now why add more?)

Additionally some things you need to know are:

  1. How many zones are active in the system
  2. Does the system use a pump to pressurize the water, if it does you need to turn it on for each zone to water
  3. Do you want info logged about how much water is put out and what was skipped (if anything because of gauges)

The metering rain gauge is bothering me, I thought I’d only need a web front end and a watering program but to track a metering gauge will require monitoring it all the time.  So I guess that will call for a daemon program that watches the gauge if you have one and accumulates water over time so you can decide if you need to water at all. Hmm, I think I leave that to last, or maybe someone else will code that since I don’t even have such a gauge, though I know they exist.

On to the next step coding the load the config.json file into memory and parse it.  Language well that will be “C” since I prefer that over Python.  IMHO Python is nice for proof of concept and quick one off scripts but I really prefer “C” for the watering program.

So, now that I have a test platform (and I still have a system in my garage that is watering the grass) its time to think seriously about the design of the software and how it will work.

The OpenSprinkler Pi has at least two open source projects one written in python and one written in C/C++.  Both of them model the OpenSprinkler Arduino software, in fact I think both can be run on both platforms.  That makes the software usable across more installations BUT at a large cost, loss of flexibility and security. The app has to run on the least powerful hardware the Arduino.  The Arduino is an  8-bit Atmel AVR microcontroller,  just bare metal is it were.  There are no OS services running on such a device. So the current app’s have to do everything on the Arduino, be a web server, serving the programming interface and have a thread that runs all the time to execute the scheduled waterings.  Simply because there is no other way for it to work on an Arduino.  Now don’t get me wrong, I like Arduino’s, and I use them for simple tasks and data gathering, they are great but they are simple devices and sometimes you need more.  Of course being an Arduino it sort of limits the damage that can be caused by crackers, you can’t reflash it remotely (at least I’m not aware of being able to reflash it remotely) so it’s going to limit what you can do with the device if you can crack it.

However the RPi is a different can of worms in this regard.  Linux on the RPi is very powerful, it is after all a fully fledged computer, not just an embedded bare metal device. Running a unified single application that has to run as root (to control the RPi GPIO pins) opens potential security holes, because on the RPi if you crack it, you can easily reprogram it to do anything you want.  Best practices on the Internet for web servers is you run them at the lowest level of authority so that in the event someone cracks the web site they can’t get control of the entire system.  Imagine if you have a single program running as root and a cracker gets control of it they could run up your water bill into thousands of dollars by turning on your sprinklers every night from midnight to 06:00 AM and turn your Internet connection into a spambot or worse.  You might not notice the damage until you get your water bill, or my my case a citation for illegal water usage!

Nope, a web server needs to have as little permissions as possible, so what can we do to address this. Well we can break the program apart, the “user interface (UI)”, “the scheduler (TS)” and a “watering program (WP)” to take the watering times and lengths and actually water the grass.

OK so the “UI” is run on a web server, check, low permissions, write data files and tell the “TS” when things need to be run.   This is not a complex program but it does require study as interfacing with humans is a skill that not all programmers have.  What makes perfect sense to a developer can be gibberish to the average human.  You need to present the information needed in a way that makes sense and builds on each step.  There could be some configuration sections that only the installer of the system needs to set, and other screens that the average homeowner needs to use to water the grass.

Next you need the “TS”, here on Linux you don’t actually have to write anything, you have cron.  Cron is a time-based job scheduler in Linux and Unix-like computer operating systems.  It can schedule jobs minute by minute, with the addition of at least one handy dandy script to enhance  cron (cron-last-sunday) you can schedule quite intricate requirements.   If you need more than that can do, you can add your own special purpose script to assist cron.

Finally the “WP here you potentially do need root access (there are ways around even needing root here but that is a topic for another blog entry).  The “WP” can only do what is in it’s data files it does not interface to the public so to speak.  The first thing the “WP” should do when it is invoked by the “TS” is quickly validate it’s data files to tell if the data files are corrupt, if they are it does nothing harmful, it simply stops.  It can however send an email to you saying “I did not water the lawn today because my data files don’t make sense.

No harm done, all that happened is the lawn did not get watered so no water wasted and all is well. If you carry a smart phone you will see that email within minutes of when the watering was supposed to start.  You can go see what the heck happened to the data files potentially fix them and still get the lawn watered in the correct time slot.  And finally the “WP” can only read and not write to the data files, so the only thing that can change the data files is the “UI”.

With the single program broken up into three (3) smaller separate bits we get higher security, something that is much easier to debug and we save time as we don’t actually need to write the “TS” program, we have it and its well tested over years of time and 10s of thousands of servers.  So 1/3 of the project is already written for you, now that is a score! ;-D

So now what, do I focus on the “UI” or the “WP”.  I’m going to focus on the “WP” because I can focus on what it is supposed to do, water the grass the way I want to water it: when (per zone), how much water (per zone), absorption rate (how much water can be applied before it runs off)(per zone).  Other things the “WP” can be aware of is a rain gauge (global), local weather reports (did it rain yesterday, is it raining now, does it have a high probability of rain tomorrow and how much is expected)(global), moisture gauges in the soil (is the soil already wet enough, or does it become wet enough while the water is being applied)(per zone or globa depends on how you install them).  Oh and lets not forget a temp gauge, because if it’s near freezing you don’t want to water then either.  And somebody I know wanted to add a wind gauge as he does not want to water if it’s too windy, as it will simply blow the water away and never get to his lawn.

As you can see the “WP” can be really simple: turn on the water per zone for x time.  Or it can be very complex: Turn on the water per zone but if the duration is longer than the absorption rate then only water as long as the ground can absorb it, cycle on to the next zone until it’s reached its absorption  limit and keep doing that until all zones are watered then loop back and put the remainder of the water in each zone, oh yea don’t do it if it’s raining now, or rained more than an inch yesterday or it’s expected at more than an 80% chance of an inch of rain tomorrow, and don’t water if its near freezing or the wind is blowing too hard. Whew, that is really really complex and yet easily within reach with an RPi as the controller, not so much with an Arduino.

Really if all you need is the simple instance, an Arduino is perfect for the job, it’s only when you get really complex do you need a bigger computer.

Once again I’ve written a really long blog entry when I meant to write a short entry, oh well, I’m done for now.  So a question, Ideas anyone, what features do you want in your sprinkler controller?

So I finally hooked up the opensprinkler hardware version 1.4 (OSPi) with my RPi and the Adafruit LCD Plate.  I tried a few things, I purchased the hardware without the RPi connector installed with the intent of using a stacking connector instead. That sort of worked, I had to cut the bottom of the case open with my dremel tool, was expecting that.  But there is a problem with this: The stacking pins are so long that the case has to be mounted on the wall with large offsets to allow enough room under the case to install a ribbon cable.  So it will work, but not really what I want to do.

Next I tried to add a pin header to the OSPi, on version 1.4  they routed all 26 pins of the GPIO to the top left corner of their board, nice.  So I installed a pin header and that worked nicely (sort of), but the RPi won’t plug in anymore, the Audio jack now hits the pin header I just installed. Whoops! So since I don’t need the audio out in this application, I decided to unsolder the audio jack. Fine that works the RPi will install the ribbon cable fits under the RPi.  So then using the dremel tool again, cut a slot for the ribbon cable to exit the case. That works.  So then I tried to put the cover back on the OSPi case.  Another Whoops, the RPi is now too high for the case to close, why?  The stacking header is some taller then the standard header.  So we have another fail. Time to remove the RPi and just install the ribbon cable and route it out of the case. Finally was able to seal the case with the supplied screws, with only the OSPi in there.

When I assembled the LCD Plate I did not use the standard 26 pin socket that it comes with, instead I installed a stacking connector so I could connect a RPi and something else to the LCD.  So the RPi is at the bottom of the stack, the LCD Plate is next and the ribbon cable is plugged in on top.  So now everything is assembled together.  I’m going to have to be creative to make a case for the RPi and LCD Plate, the ribbon cable sticks up above the display but that I “think” I can work around.  I’ll worry about that later.

OK, plug the RPi into the monitor and keyboard (which normally won’t be used but hey I”m testing stuff right now ;-P ), and plug in the micro USB connector for power.  Yea, the RPi lights up, no magic smoke being released anywhere.  Score.  Next I look at the OSPi and it’s powered up too, at least the 5V DC section is.

So I run a quick python program to  check the I2C port, as there should be at least 2 things hanging off it, the RTC @ device 68, and the LCD Plate at device 20 so time to run:

i2cdetect -y 1

The result looked like:

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- UU -- -- -- -- 
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --

Yep both there 20 is the LCD and 68 is the RTC.  Next setup the RPi to use the RTC, easy instructions are avalible with the OSPi. :-) Then run the LCD Plate test code and yes it is working fantastic.

Now I need to test the rest of the OSPi hardware, the sprinkler control section.  Thats going to be a bit harder, I don’t want to go buy a bunch of sprinkler control valves, that is expensive so what can I hook up that can: 1) Work on 24vac 2) See a visual to tell it is working. 3) Is cheap since I only need it for debugging code.  Hmmm, first thought is light bulbs, they run on AC but have you ever tried to find 24VAC light bulbs? Turns out that is both hard and expensive.  BUT 12V light bulbs are plentiful and pretty cheap < $10 will buy 8 low voltage yard light bulbs.  But can I rig 12 VAC lights to a 24VAC source? Not likely 12lights will turn into a fuse at 24V and blow out.  Could add a big honking resistor and waste a lot of heat but thats just stupid and adds cost.  Wait will the OSPi work on 12VAC?

Yep turns out the OSPi will work off of 12VAC for testing, it needs an AC source of 24VAC to run sprinkler zone valves but the internal DC2DC converter that makes the 5VDC power for the RPi is happy from 9VAC – 24VAC. Perfect  Off I go and I buy 8 light bulbs, a circuit board to mount them on, and a 12VAC wall wart.  Solder up a test board with the lights and plug up the 12VAC transformer to the OSPi.   Sure enough, the OSPi powers up fine, makes 5VDC to run the RPi and all is good.  Finally :-)

Well spoke a bit too soon. :-(  I ran some test code to turn on zone 1 light, it lit for a split second and the RPi rebooted!!!!  What the heck?  Turned out the wall wart was too low an amperage transformer, worked fine just running the OSPi, RPi and the LCD but turn on a 4W light bulb and the voltage drop was too much for the regulator.   So plugged in both the 12VAC transformer and the micro USB connector so the RPi and the LCD and the 5VDC side of the OSPi got juice from the micro USB. Tried it again, yea, it works finally I have a functioning test platform, with a slightly butchered case.  Let the coding begin.

When its really hooked up in my garage it will be supplied from the mondo 24VAC wall wart so that won’t be a concern.

OK, it is the 3rd Monday of June and like clockwork, the water came on and all worked as expected with a simple cron entry.

But lots going on that will generate a longer blog entry soon, the rayshobby / open sprinkler folks have some really nice hardware in the opensprinklerPi hardware.  So I don’t need to design my own board, I can use their open hardware project.  That said I don’t care for any of the software that currently runs on it, it looks like a clone of the existing weekly software that comes on any < $300 USD controller.  Yes you can program it via a web browser and/or a mobile device but it still lacks monthly levels of control.  Also the layout of the hardware restrict adding displays easily.  I added a header and will use a 26 pin ribbon cable to remote the PI and display from the opensprinklerPi hardware.  More about that later.

 

Have a nice day, I am since I no longer have to worry about my lawn getting watered on the correct day…..

So carrying on with my sprinkler controller project I need an easy way to run things on the 1st Monday of a month and the 3rd Monday and only then.  Hmmm, I can do that with cron by looking up the dates and entering each day but that is a manual process to look that up and then enter 24 entries in the crontab…. Must be a better way so I go surfing the Internet and what do I find but a nice bash script called run-if-today.  You feed it the day of the week and what week of the month you want the entry to run. Perfect  makes the crontab 2 lines vs 24 lines!

# m h dom mon dow command
00 06 02 06 * sudo /home/pi/bin/waterallzones.py
00 06 16 06 * sudo /home/pi/bin/waterallzones.py
00 06 07 07 * sudo /home/pi/bin/waterallzones.py
00 06 21 07 * sudo /home/pi/bin/waterallzones.py
# etc, on and on until the end of the year

OR

# First monday of month jobs
00 06 * * 1 run-if-today 1 Mon && sudo /home/pi/bin/waterallzones.py
# Third monday jobs
00 06 * * 1 run-if-today 3 Mon && sudo /home/pi/bin/waterallzones.py

Much better, it will run forever on the first and third Monday. Yea.

So credit where credit is due: I found this on github cron-last-sunday it’s under an MIT license and Copyright (c) 2014 Manuel Gutierrez.  Nice work.  I had a friend Debianize the script into a nice easily installable Debian package, which I need to figure out where to stash it so others can install it on their RPi, because it really simplifies using cron for certain conditions.

 

It Works :-)

Posted: June 2, 2014 in Uncategorized

OK, it’s Monday morning here in the Dallas area and for the first time in over a year the sprinklers came on automatically.  The new system I installed yesterday worked as expected.   I don’t have to worry about remembering to get up and press a button at 06:00 AM on the first and third Monday of the month anymore.  All good.