, ,

A month ago, I decided to work on this summer’s irrigation project for one month ending June 20th

Thanks To Those That Went Before

  • cmpxchg8b – In their reply to the Adafruit forum discussion post M0 wait for interrupt (worth a read), they provide the useful idleSleep() function. THANK YOU.
  • monohelixlabs – for  the DataFeeds iPhone app.  By using this app, I was quickly able to view the Adafruit.io data feeds on my iPhone.
  • Adafruit – for the great products, tutorials, libraries, support, and incredible people.  Adafruit has made it extremely easy for me to evolve and learn through projects like this one.

The Goal

The goal of this post is to document the June 20th status of my 2017 Auto irrigation project.

Open Source

The Arduino code I used for the Moisture Puck and Controller are located at this GitHub location.

(note to myself?)

There were a lot of test Arduino sketches that are not relevant to this post in the folder containing the sketches I push to GitHub.  To ignore these sketches, I added them to the .gitignore file.  I did this by:

  • opening a terminal on the Arduino folder
  • ls > list.txt
  • sed ‘s;$;/*;’ list.txt > new_list.txt

  • copy/paste the contents of new_list.txt into .gitignore
  • deleting the sketches that should not be ignored for the Controller and Moisture Puck.

The Project

All this technology and I’m wasting time hand watering three different growing areas in my backyard (see this post for a description of my backyard watering areas).

What I Set Out to Build

I set out to build an automated watering system.  The soil needs water, it automatically gets watered.

What I Built

Instead of auto watering, I opted to get a better feel how the system works (worts and all!) by tying into the Internet through Adafruit.io.  I did not break into the irrigation pipes and install solenoid valves.

This is what I built:


Overview of Interactions

 The actors in this story include:

  • The Moisture Puck.  The Moisture Puck uses a moisture sensor to measure how wet the soil is.  The Controller sends a request (1) for moisture info.  The Moisture Puck returns (2) the moisture level, the battery level, and how hot the RFM69 chip is.
  • The Controller.  The Controller controls the conversation between itself and the Moisture Puck as well as communicating with the Adafruit.io service (3).  When the Controller receives moisture information from the Moisture Puck, it publishes the moisture level, battery level and RFM69 chip temperature as data feeds to Adafruit.io.
  • Adafruit.io.  There are four data feeds.  One for each of the values captured from the Moisture Puck.  A fourth feed – I call this the water feed – is used as a “control” feed.  The firmware in the Controller subscribes to the water feed.  When a data value is added to the water feed, the Controller receives an update.  This causes the controller to talk to the Moisture Puck, get a reading, and then publish the reading on Adafruit.io.
  • IFTT.  YIPPEE! For me, Adafruit made it so I can write applets in IFTT that include Adafruit.io data feeds.  The applets I built include:
    • an applet that gets the sunrise information (when the sunrise occurs, what the current weather condition is).  The sunrise time and weather condition is written to the water feed.  As I noted above, writing to the water feed triggers the Controller to request a moisture reading which is then written to the Adafruit.io data feeds.
    • a similar applet that gets the sunset information.
    • an applet that connects SMS to the water feed.  I send a text message to the IFTT SMS number….and…yah…you guessed it…this triggers the Controller to request a moisture reading….

 The Moisture Puck In the Garden


Moisture Puck In Action

Hook Up

The hookup is very simple.  The soil moisture is connected to the Feather’s 3V, GND for power.  It uses A0 to get moisture readings from the moisture sensor.

NewImageThe battery is wrapped with gaffer tape to give a layer of protection.

What Worked

  • I really liked the Feather M0 RFM69. The radio can send/receive packets at the distance needed.  The Cortex M0 has been very stable, has strong power management (assuming I can figure out more 🙂 ), and has great documentation.
  • While a little bit too finicky for my tastes, the Feather Huzzah has proven to provide a steady Internet connection.
  • Adafruit.io made it extremely simple and easy to integrate in powerful apps such as IFTT.  Compared to my recollection of what was available just 5 years ago, I find this simplicity amazing.
  • Taking and interpreting sensors readings worked seamlessly.
  • Separating the enclosure into two boxes – one to hold the battery and one to hold the feather.
  • Attaching the moisture probe to a cable instead of to the enclosure.  I was able to better place the probe close to plant roots.  I was excited to repurpose an audio cable that was sitting within a snarl of cables in our garage.

What Needs Work

  • An enclosure that is completely waterproof inside.  Adding a coating of XTC stopped leakage coming in between the plastic layers.  What needs work is a top and bottom piece of a (box like) enclosure that is easy to take apart – say for example to recharge the LiPo battery in the case of the Moisture Sensor – but is also waterproof.  What I am currently doing is wrapping the sides of the box in gaffer tape.
  • I’m having to recharge the LiPo after 2.5 days. This is way too often.  I was able to improve the battery life by ~ 27% using the Cortex M0’s IDLE mode 2 (see the SAM D21 Data sheet, section  I was not able to set the M0 into STANDBY mode. STANDBY mode is a lower power mode than IDLE mode 2.  I am investigating what should be done to get STANDBY mode to work.
  • Using text messages as the user interface.  I send a text message on my phone and then look at updates to the feeds within the DataFeeds iPhone app.  I should be able to send different text messages and receive immediate responses….questions like “what is the current moisture level/battery level…” or get a text when watering is needed.

What Didn’t Work

  • I didn’t get around to connecting a solenoid valve to the irrigation lines in order to start auto watering.  I ran into challenges getting the circuits working reliably on a Feather proto board.  These challenges shifted my focus to Internet integration.  I don’t see any showstoppers at this time. I just haven’t got around to getting auto watering built and running.  It’s probably just as well because there is still tweaking of the Moisture Puck.  I’m not at 7 x 24 x the days of spring, summer, and fall of reliable moisture readings.
  • Saving additional battery life by putting the M0 into STANDBY mode. As I mentioned earlier, I plan to spend time figuring this out.  I also want to try solar power/recharge once I get all the parts.

Initial Notes on Power Management

When I didn’t add power management code, the moisture puck stopped talking to the Controller after 44 hours.

I added in this code without knowing much about the AtSamD21 power settings:

   IDLESLEEP - thanks to cmpxchg8b: https://forums.adafruit.com/viewtopic.php?f=57&t=104548&p=523829&sid=22b8d0735f65f3f53a0f1e8781c886e6#p523829
void idleSleep()
  // Select IDLE, not STANDBY, by turning off the SLEEPDEEP bit

  // Select IDLE mode 2 (asynchronous wakeup only)
  PM->SLEEP.bit.IDLE = 2;

  // ARM WFI (Wait For Interrupt) instruction enters sleep mode

The Controller started receiving data on 6/15 at ~3:47AM.

The last data the Controller received was on 6/17 at 11:20AM:


Progress.  Based on the same hourly readings used in the experiment where the moisture puck lasted for 44 hours with no power management code, power to run the moisture puck was increased to 56 hours.  This gave 27% more time (i.e.: to calculate percent increase: (56-44)/44 – yah, yah … you know this it is so basic..yah, yah…ooh – umm..hold on…look at this picture of the Grand Canyon – HOW A-W-E-S-O-M-E:

[Side note: Here’s my challenge with remembering basic math…I mean…the image of the Grand Canyon or calculating percent increases?  My mind chooses images of the Grand Canyon every single time.  But I diverge.]…

Looking at the AtSamD21 data sheet, section Sleep Mode Controller,  Table 15-4: Sleep Mode Overview, Idle 2 (the sleep mode used in the current firmware) uses more power than Standby.  Hmmm…the data sheet goes on to note that standby mode (uses less power than idle mode) should react to an interrupt from the RFM69 chip:  Any peripheral able to generate an asynchronous interrupt can wake up the system. For example, a module running on a Generic clock can trigger an interrupt. When the enabled asynchronous wake-upevent occurs and the system is woken up, the device will either execute the interrupt service routine or continue the normal program execution according to the Priority Mask Register (PRIMASK) configuration of the CPU.

I will write a sample client/server that:

  • First, uses the power saving code I am currently using to make sure it works in this mode.
  • Second uses deep sleep/stand by mode.
Hmmm…based on the above comment, I thought this:
void idleSleep()
  // Just put in deep sleep:
  // Select IDLE, not STANDBY, by turning off the SLEEPDEEP bit

  // Select IDLE mode 2 (asynchronous wakeup only)
  //PM->SLEEP.bit.IDLE = 2;

  // ARM WFI (Wait For Interrupt) instruction enters sleep mode

but alas… the code just waits…and waits…and waits…and waits…and waits…. so for now, power management means putting the M0 to wait for interrupt when the chip is in idle  mode 2.  Too bad.

Talking To The Controller from my Phone

Given the iPhone is evolving into my trusted assistant, I wanted to communicate to the Controller through text messaging…the same way I communicate with…well…humans when I use my iPhone…

My first attempt at this is to use Adafruit.io and IFTT.  I can send a text message to the IFTT SMS service to get the Adafruit.io IFTT service to post the message on the water feed. Posting the message on the water feed invokes the function within the Controller that is subscribed to be notified of changes to the water feed.  The Controller gets a reading from the Moisture Puck.  The Controller then publishes the moisture, battery level, and RM69 chip temperature readings to their Adafruit.io feeds.  Also, I created an IFTT applet that fires when the battery level is less than 3.6V.  This way, I will know when to charge the battery.


I then look at the data feeds within monohelixlab’s data feeds iPhone app.

What Worked


I started using Adafruit’s mqtt library.  Stuff worked.  Then I thought…ooh…neato….Adafruit has a simpler library – Adafruit.io – so I mucked up the code to try the simplified version.  

Frustrated sadly, the Adafruit.io library calls seems to cause a soft reset of the Huzzah.  Honestly, many things cause a soft reset.  With that said, at this point I won’t spend time debugging the Huzzah.  I want to change to the newer Feather Huzzah32 but they are (still!) on back order…so…back to the lower level mqtt library…which works fine.

Bit Manipulation

As I was exploring, my old friend – bit manipulation – came for a visit.

Bit manipulation techniques reminds me of learning multiplication tables.  At least for me.  Perhaps for some it “just appears” in their skill set.  For me, it is practice.  When it comes to practice, I’m finding if I can minimize and simplify what I practice, I am better prepared to make the technique a constant part of my toolset.

I want to use bit manipulation more for keeping track of state – as well as the obvious setting of registry bits – because to me it becomes more intuitive than perhaps using an enum of states.  In the case of the moisture puck firmware, bit manipulation is used disable deep sleep.  From this ARM post, deep sleep is enabled by turning on the deep sleep mask:

SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; /* Enable deepsleep */

in the moisture puck firmware code, the deep sleep mask is disabled. The mode is then set to idle mode 2:

  // Select IDLE, not STANDBY, by turning off the SLEEPDEEP bit

  // Select IDLE mode 2 (asynchronous wakeup only)
  PM->SLEEP.bit.IDLE = 2;

ON: Turn a bit on with |= .

OFF: Turn a bit off with &= ~<variable set to all zero’s except a 1 on the bit that is being turned off>


uint8_t flagByte = 0x0;
uint8_t firstBit = 0x1;
flagByte |= firstBit;
if (flagByte & firstBit){
  // the firstBit bit of the flagByte variable is on

Is the bit OFF?

uint8_t flagByte = 0x0;
uint8_t firstBit = 0x1;
if (~flagByte & firstBit){
  // the firstBit bit of the flagByte variable is off