An IoT project: Monitoring soil moisture (Phase 3) – Adding Watering Capability (Part 2)

In the previous articles, we covered the implementation of a water pump station as a separate module and did some discovery works on adding WiFi capability for the Soil Moisture Monitoring system. They can be found here and here respectively.

In this article, we will continue to look at integrating the water pumping system with the main system and hook up the pipes to the pots.

Building an irrigation system

Before we start connecting the water pump station to the main system, we need to first build the pipe network that will ensure water is delivered to the right place.

At first, I had the idea of using both silicone tubing and Lego for this aspect. The silicone tubes would serve as the water delivery medium while Lego will be used to build the emitters and be installed around the pots.

For the Lego water emitters, I’ll admit I didn’t sketch out to guide me in the construction process. Just like any creative endeavours that I partake, which includes writing, my process is akin to a “gardener” or “pantser”. You can read up more on what these means here. Put it simply, I have some rough ideas in my head, and then set out to explore and experiment trying to achieve a desired outcome.

But I digress.

So, I had the bricks laid out in front of me and I went exploring. It took me several tries before I settled on a final design that I’m happy with.

The idea behind the above design is that the silicone tube will go into the orange cone-shaped bricks. The good thing about those bricks is that the tube could stay relatively secured. This emitter will be attached at a height. Once water exit the tube, it will follow the slope down onto the soil below.

However, my experiment with trying to get water to flow down the slope failed. Either the water jets out the when there’s a slight water pressure or the water bunched up together and roll off the slope as a globe.

I gave up on using Lego to build the water emitter and went out to a hardware store to get actual water emitters. At the store, I could only find 3-way coupling and 0-6l/h line-end dripper by Claber in stock. I got both. In hindsight, the dripper is actually redundant.

Once home, I built a ring-shaped irrigation system and then testing it out.

I used the dripper first to build the irrigation system, which you can see in the picture above sitting on top of the bowl. During testing I realise it was the wrong component. The water pressure from the pump was too much and water was bursting out any openings it found. I had to shut the system immediately because there were electronics around me. In hindsight, I could have reduce the speed of the pump to maybe a fraction of 100% and it might have worked.

Anyway, the next day I switched out the dripper for the coupling and switched to another plastic container since we do eat from the bowl. Not very hygienic to use it for testing.

This time, it works a lot better. Even at 100% pump speed, water didn’t burst out at the seams and dripped into the container gracefully.

And, this is where the integration with the main system comes in. The remaining part of the article will focus mostly on the software aspect of the integration.

Water Pump Integration

Previously, we were able to get the water pump station to work with an Arduino Uno. We figured out how much water the pump could move for a given time in second. This give us the confidence to start deploying it into the real world.

First, we will connect the Grove cable to the I2C on the Arduino shield before we could do anything else.

Connect the Arduino to the computer and load the original soil moisture monitoring Sketch into the Arduino IDE.

We will get the Sketch ready to support the motor driver.

Add the following #include directive.

#include "Grove_I2C_Motor_Driver.h"

And the following #define directive.

#define I2C_ADDRESS 0x0F

Then, we add the following to the setup function after Oled.begin().


The following is how the setup function should look like.

void setup()
  Serial.begin(115200); //Open serial over USB

  pinMode(sensorVccPin, OUTPUT); //Enable D#7 with power
  digitalWrite(sensorVccPin, LOW); //Set D#7 pin to LOW to cut off power

  pinMode(sensor2VccPin, OUTPUT); //Enable D#8 with power
  digitalWrite(sensor2VccPin, LOW); //Set D#8 pin to LOW to cut off power

  pinMode(alarmVccPin, OUTPUT); //Enable D#5 with power
  noTone(alarmVccPin); //Set the buzzer voltage low and make no noise

  pinMode(button, INPUT);

  if (Oled.begin())
    Serial.print("Fail to initialise OLED");

After the above, the Sketch is now ready to trigger the pumps when the conditions are right. We will first add a few global variables and functions to handle the water pumping process and stopping the pump.

For global variables, we will need the following declared at the top of the Sketch together with the rest. Note: Motor and pump are used interchangeably in this project because the pump is just a special type of motor.

bool motorRunning = false; //Keep track if the pump is running.
unsigned long motorRunTime = 0; //Keep track of when the pump starts running.
unsigned long motorToRun = 1000 * 5; //5 seconds for a more meaningful test run

The following function determines if the pumps should activate.

For now, since we are just testing, we will just go with a simple check to see if moisture level is below certain value. If it is, then pump water. Given what we know about the soil moisture sensor, dipping it into a bowl of water will give us a reading of more than 800. Taking it out of water will cause the following function to run.

void waterPlantIfNeeded()
    if (moistureLevels[1] < 800)
        Serial.println("Pumping water now...");

The following function handles the actual water pumping process. The reason for running motor 2/pump 2 for testing is because it is the pump that I have connected the irrigation system to.

void pumpWater()
    if (!motorRunning)
        Serial.println("Running pump 2");
        motorRunTime = millis();
        Motor.speed(MOTOR2, 100);
        motorRunning = true;

The following function stops the pump when it has run for more than the specified period.

void stopPumpingWater()
    if (motorRunning && millis() > motorRunTime + motorToRun)
        Serial.println("Stop pumping water");
        motorRunning = false;

Then, in the loop function, we added the call to the waterPlantIfNeeded() function in the if statement that does the sensor reading as shown in the screenshot below.

After that, we added the call to stopPumpingWater() at the end of the loop function.

And this is time for testing.

However, the testing didn’t go so well. The motor driver will fail to initialise quite often. That is even after multiple restart. And when it did finally run, the OLED display will fail to run. That was when I’m reminded of the bug in the firmware of the motor driver I mentioned previously.

It was time for firmware update.

Updating Motor Driver Firmware

I came prepared for this step. I ordered and got the Pololu AVR Programmer v2.1 a few days ago that could be used for updating the firmware.

So, I went ahead to take apart the housing to gain access to the motor driver. I hooked up the motor driver to the programmer and connect the programmer to the computer. For the programming of new firmware, we can use avrdude.

On the mac, you can download and install avrdude via homebrew.

Then, do connect the motor driver to a separate power source too since the AVR programmer does not supply sufficient power. I made the mistake of not connecting the motor driver to a power supply and waste half an hour trying to figure out why I couldn’t flash the new firmware in.

You can download the latest firmware for the motor driver here at the official repository on Github.

You should also download and install the Pololu AVR Programmer v2 app onto your computer as before we could flash the firmware on the motor driver, we will need to know which port we are using. The app could tell us that.

Once you have the firmware downloaded, avrdude installed and the AVR programmer is running properly, open terminal and navigate to the folder where the firmware is residing.

Then, use the Arduino IDE to find out what are the ports available on the AVR programmer that you can use.

In my case, the port I have to use for firmware flashing is /dev/cu.usbmodem003268172 as it is the programming port identified in the AVR Programmer app.

To flash the firmware, use following command. Do supply the programming port to use before pressing the ‘Return’ key.

avrdude -p m8 -c avrisp2 -P <the programming port to use> -U flash:w:./mega8motor_v4.hex

If the command is accepted, you should see something in the terminal that is similar to the screenshot below.

Now that the firmware is updated, we could continue our testing and do the rest of the implementation…

More troubleshooting…

It turns out updating the firmware on the motor driver isn’t enough even though the Arduino is able to detect the motor driver and trigger the pumps. After the motor driver is initialised, the Oled display refuses to work. Even though the Oled display initialised correctly without any error, any commands sent to it didn’t yield anything.

Below a screenshot of one of my attempts to investigate what’s going on.

I thought initialising the Wire library manually would help. The Wire library is basically an Arduino library that allow us to work with I2C connections/devices.

There were times when it works. I could use the Oled display after the motor driver is initialised. The first time it worked, I thought everything was fine.

Subsequently, the Oled display continued to fail to work and I spent half a day trying to understand why.

A chance Google search led me to this page on displaying data on oled that is part of the Beginner kit for Arduino.

At the end of that section was a Breakout guide. It describes what to do if the Oled display cutout from the kit.

With that, I went searching for the library on my computer and making the change to U8x8lib.cpp

Then, to test that the Oled works this time, I added a function called displayMessage(String message) to handle the display of text regarding the state of the Sketch. In the setup function, I added calls to this function with the relevant text.

After that, I recompiled the Sketch and upload it to the Arduino. Once the deployment is completed, I kept a close watch on the Oled display. Soon, it became clear that it worked. The Oled display was showing me the text I set when calling the displayMessage function.

A further test of pressing the button I implemented previously to turn on the display revealed that it is indeed properly now. And I also know that the motor driver was initialised properly because I heard the pumps doing test spins as implemented in the setup function.

With that, it would appear that a bug in the U8x8 display library gave us so much woe.

Going Live!

With the Oled display and motor driver working now, it was time to go live with the automatic water pumping. If you are wondering about why the tubes are not in a water bottle/container, it’s because the picture was taken shortly after the successful test run. I didn’t want any water involved just yet.

What’s next?

Even though the water pumping station is integrated, it is not the end of this project yet. We still need to implement the WiFi connectivity and this will be covered in another article soon.

We also need a web-based dashboard and some other quality of life improvements such as buttons or switches to turn on/off specific pumps while the rest of the system is running or an API for us to manually trigger the water pumping via the internet.

If you would like to have a fuller picture of what I did, the source code to the Sketch can before found here.