An Arduino project that automatically waters plants has been done many times.  This summer I finally decided to tackle on my own.  I’m writing up my first attempt at this project in two posts:

  • wirelessly connecting moisture readings from a spot in my garden. (This post)
  • automatically watering areas in the garden based on the moisture readings.
I say first attempt, because as I finish up this attempt I can see several areas where improvements can be made.  I mean, whoa.  I had no idea what the difference between measurements using two galvanized nails versus a sensor used by horticultural researchers….and if the idea is only for watering my backyard vegetables and not a cash crop – how much do I care?   So…what is the best my auto watering solution needs to be?

The Goal

The goals of this post are to:

  • build a moisture sensor gizmo that collects info on the moisture of the dirt it is stuck in and sends it to a receiver hooked up to my Mac.
  • build a receiver that reads the info sent by the moisture sensor gizmo and displays the info on my Mac.
  • identify improvements for the second prototype.

Thanks To Those That Went Before

  • Felix Rusu (  for his work on the Moteino.  The Moteino is an open hardware Arduino + RFM69HW radio  development board very similar in concept to Adafruit’s Feather series.  In fact, it is Felix’s RFM69xx library that Adafruit – and hence most of us – use within the Arduino IDE to talked with the radio. 
  • Thanks to Adafruit for great development board products like the Feather series.  Adafruit’s support is excellent.  I had a challenge with using a serial port with the feather (see below).  Thanks to Mike in Adafruit support, not only was my issue quickly fixed, but I learned stuff about the micro controller used by the Feather that I hadn’t realized.  THANK YOU MIKE.  I also want to thank Tony DiCola for removing any complexity in using the Watchdog timer by writing Adafruit’s Sleepy Dog library.  THANK YOU TONY.

Open Source

The Arduino and Fusion 360 files are available at this GitHub location.

The Stuff

The stuff I will build for this project include:

TX Box

This is what I’ll be calling the enclosure and the stuff inside that sends moisture readings.  The TX Box includes:

  • Moisture Sensor:  At the risk of stating the obvious, the role of the moisture sensor is to tell the electronics how moist the soil is.  So I ask myself, what probe should I use?  The more I learn about growing healthy plants aided with technology, the more respect I am gaining for the quality of the probes used.  At this point, it is not clear to me if two galvanized nails are good enough – or the accuracy/reliability obtained by using these sensors (particularly the EC5 and/or the 10HS) used by researchers  are required.  POSSIBLE SECOND PROTOTYPE IMPROVEMENT: explore the benefit of a more accurate/reliable sensor.  For this prototype, I am using these inexpensive moisture sensors being sold on eBay

The moisture sensor has three clearly marked pins, one for GND, VCC, and data.  The instructions consist of a sentence on the eBay page: Output voltage :0-2.3 V [2.3V is completely immersed in water] voltage, 5V power supply, humidity, the greater the output voltage increases.

  • A Lithium Ion Battery.  I don’t have active electrical plugs within reach of all areas of our backyard.  This leads me to choosing either solar or battery power.  I felt using a battery would be the simplest solution.  I wanted to recharge rather than replace the battery and I wanted the battery to power the remote measuring sensor for as long as possible.  For these reasons, I chose a lithium ion battery.  POSSIBLE SECOND PROTOTYPE IMPROVEMENT: Determine if solar power will work “better” if it does, replace battery power with solar power.

  • Adafruit’s Feather 32u4 with RFM69HCW:  YIPPEE! for this development board.  It dramatically speeds up how much time it takes to complete this prototype.  Besides having a small and light form factor, it is compatible with the Arduino IDE and Arduino’s pin outs.  It integrates in the terrific RFM69HCW radio that I have played with before – a great solution for home automation tasks that are not suitable for bluetooth or wifi.  A battery connector supports me plugging in a LiPo Battery and there is a micro usb port for communicating with the Arduino IDE.  If that wasn’t enough – like all the other Adafruit development boards – there are a wealth of libraries and documentation.  Add Adafruit’s excellent support, and the price is a bargain over what I’d have to do to bumble my way through putting the chips together.  Perhaps if I get serious about a final future build I might think it worthwhile to design my own PCB.  But for prototyping?  Like I said – YIPPEE!!!!!


  • Waterproof enclosure:  Designed in Fusion 360 and printed on my 3D printer. Here’s some images of the enclosure:


I’m not going to detail the design or printing.  I will however point out some of the “best practices” I am evolving in my knowledge and practice of 3D printing design and print:

    • Have a realistic model for each component. By realistic, I mean not just the dimension of the object in the physical space, but also any room needed for placement on the side or cables, which is the case for the LiPo battery.  The simple models I made were based on measurements I made with my caliper.  When I relied on parameters I measured without a realistic model, I ended up having to reprint/adjust much more often. 

FeatherFusion360ModelMoistureSensorFusion360Model LiPoBatteryFusion360Model

 I found the Fusion 360 model for the Feather here.

    • Given the enclosure is going to be placed outdoors within an irrigation system, minimize any openings.  Now, this sounds obvious.  Yet my original design had an opening for the micro-USB port.  I ended up with two slits for the moisture sensor.  Hmmm… come to think of it, including the moisture probe inside the box is not the best idea.  Much better would be to have small holes for about 22 AWG wire that goes between the enclosure and the moisture probe.  POSSIBLE SECOND PROTOTYPE IMPROVEMENT:  Move the probe to outside the enclosure.  This way, the moisture sensor can be as close to where measuring takes place.  Whereas the box containing the feather and battery can be placed at a close by location.  Notice in the third picture on the right of the enclosure pictures that I ended up placing the enclosure into a ziplock bag and taped it up with waterproof tape.  I also put silicone around the area where the prongs went through the slits within the enclosure.

Receiving Station

When I evolve the prototype to include automatic watering, the receiving station will morph into something similar to the TX box only for receiving the packets from the TX box(es), deciding whether watering is needed, and turning the water hose on/off.  This stuff is reserved for the next stage of this prototype.  Until then, the receiving station is simply:

Arduino Code

A Gotcha – Using Serial Ports

I had no previous experience using a 32u4 based board with the Arduino IDE.  “Those that went before” know there is a hardware difference between the Arduino Uno and the Feather that “bit” me in the handling of serial ports.  Buried a bit in Adafruit’s documentation is this info gem: “…UNO-type Arduinos have a seperate serial port chip (aka “FTDI chip” or “Prolific PL2303” etc etc) which handles all serial port capability seperately than the main chip. This way if the main chip fails, you can always use the COM port. M0 and 32u4-based Arduinos do not have a seperate chip, instead the main processor performs this task for you. It allows for a lower cost, higher power setup…but requires a little more effort since you will need to ‘kick’ into the bootloader manually once in a while.”

Manually restarting the boot loader is one of the behavioral differences discussed in Adafruit’s documentation .

The other is when the Serial port becomes available.  I wasn’t able to get Serial.print()’s in Setup() until Mike in Adafruit support pointed out:


Are you using:

while ( ! Serial ) { delay( 10 ); }

before you call Serial.begin()?
That will make the code wait until the USB registration has finished before trying to print anything.


This race condition is noted in the if(Serial) Arduino documentation.  Looking back, I should have looked into why the examples that used the Serial functions started in setup() with while (!Serial);  instead of deleting because I didn’t have an understanding why it was there.  A D’OH moment on my part.

The Data Sent

The values to be passed from the TX box to the Receiving Station are stored within the valuesStruct_t structure.  A union data type is then used to map the value view to a string of bytes view.  The string of bytes is then sent to the Receiving Station where the bytes are picked up and then mapped back to values.  The values include:


// Define a struct to hold the values
struct valuesStruct_t
uint8_t firmware_version;
unsigned int reading_number;
float battery_level;
int moisture_reading;
unsigned int sleepMS;
// Define a union type to map from the struct to a byte buffer
union txUnion_t
valuesStruct_t values;
uint8_t b[sizeof(valuesStruct_t)];

  • Firmware Version: This is currently set to 1.  The value of the Firmware Version is used by the Receiving Station to figure out if it understands the valueStruct_t structure.
  • Reading Number: The Reading Number increments each time the data is sent.  This way I can have an idea if data has been dropped as well as how many packets have been sent.  The data type is an unsigned int.  As noted in the Arduino Reference under Coding Tip: “When variables are made to exceed their maximum capacity they “roll over” back to their minimum capacity…”
  • Battery Level: The battery level lets me know how fast the battery is being used up and when I need to recharge it.  Adafruit provides the circuitry and code to easily report on the battery level.  From Adafruit’s documentation under Measuring Battery:  “Lipoly batteries are ‘maxed out’ at 4.2V and stick around 3.7V for much of the battery life, then slowly sink down to 3.2V or so before the protection circuitry cuts it off. By measuring the voltage you can quickly tell when you’re heading below 3.7V.
  • Moisture Reading: This is the reading from the moisture sensor read from one of the Feather’s ADC ports.
  • Sleep (in milliseconds): The amount of time between readings.

The Data Received

The data received is the same as the data sent.  Two additional pieces of info are used that come from the RFM69CHW firmware:

  • RSSI: To get an idea how how strong the signal is between the TX box and the Receiving Station.
  • The Node ID:  In order for a Receiver to know which RFM69* radio sent a packet, a Node ID is sent with the data packet.  In the Receiving Station’s Arduino sketch, the Node ID is set using #define NODEID 2.  In the evolved experience where there are three or more TX boxes, the Receiver will need to know which TX box sent the packet in order to identify where to irrigate.

Power Management

 The experience scatters one or more rechargeable batteries around my garden.  I want to minimize the time between having to recharge the battery (or batteries).  Stuff I want to optimize to meet this goal include:

  • Minimizing the amount of power used by the Feather.
  • Minimizing the power used by the RFM69CHW radio when sending data packets.
  • Minimizing the number of data packets that are sent.

Minimize Power Used By the Feather

Leave it to Adafruit’s document and SleepyDog library to make it easy for me to minimize the power the micro controller gobbles up. Instead of using delay(), the SleepyDog library uses the watchdog timer.  Adafruit’s Feather documentation under Radio Power Drawdoes a great job showing how power use is minimized using the watchdog timer.

int time_Between_Readings_in_ms;

void loop() {
// Since the watchdog timer maxes out at 8 seconds….
int number_of_sleeper_loops = 4; //time between taking a reading is 4 * 8 seconds = 32 seconds.
for (int i = 0; i < number_of_sleeper_loops; i++) {
time_Between_Readings_in_ms = Watchdog.sleep(8000);

The watchdog timer’s max amount of sleep time is 8 seconds. If you set the time between readings to be  >8000 and <= 32767 (the maximum positive value for an int), the watchdog timer is set to 8 seconds. If you set the time between readings to be >8000 and <= 32767 (the maximum positive value for an int), the watchdog timer is set to 8 seconds.  A negative signed int will set the watchdog timer to 15ms.

Minimize Power Used By the Radio

You Are Getting Sleepy….

When transmitting the packet within the transmitReadings() function, I make a call to radio.sleep().

if (radio.sendWithRetry(RECEIVER, txData.b, sizeof(txUnion_t))) {

As noted in Adafruit’s Feather documentation under Radio Power Draw :  “If you put the radio to sleep after transmitting, rather than just sitting in receive mode, you can save more current, after transmit is complete, the average current drops to ~10mA which is just for the micro controller.

The Power Level

This line in the sketch:


where the number (in this case 31) sets the amount of power used for transmission between 5 and 20 dBm.  The value for the power level can be from 0 – 31.  The amount of power increments in steps of 1 dBM.  So for example radio.setPowerLevel(1) = 6 dBm.  If that is the case, I was wondering why values go from 0 – 31 instead of 0 – 15 since radio.setPowerLevel(15) = 20 dBm.

I’m assuming (note: I haven’t validated) the reason given in this post is correct: “…the rfm69hw puts increments power in one db steps from power settings 0 to 15, then starts over at 16 and increments up again from 16 to 31.”

I tried a variety of tests where the radio of the TX box was set at a variety of power levels and determined given the distance, I needed to leave the power level at the highest setting.

Minimize Number of Data Packets

For this prototype, the only thing I am doing to limit the number of data packets sent is to send at a period of 32 seconds (instead – for example – every time through the loop() ).  POSSIBLE SECOND PROTOTYPE IMPROVEMENT: Send a packet only upon request of the Receiving Station.  There is no point to sending packets unless the TX box is asked for an update since the intent is to take action and water (or not) depending on the moisture sensor reading(s). 

Running the Code

ßHere is an image of an example run:

 A moisture reading in the 500’s means the soil does not need watering.  But more on making a decision based on readings in the next post.

Second Prototype Improvements

I’ll collect all the improvements I noted above to be considered for an evolved prototype:

  • Explore the benefit of a more accurate/reliable sensor.
  • Determine if solar power will work “better” if it does, replace battery power with solar power.
  • Move the probe to outside the enclosure.
  • Send a packet only upon request of the Receiving Station.

Thanks for reading this far.  Please find many things to smile about.