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().

 Motor.begin(I2C_ADDRESS);

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())
  {
    Oled.setFlipMode(true);
    Oled.setFont(u8x8_font_chroma48medium8_r);
  }
  else
  {
    Serial.print("Fail to initialise OLED");
  }
  Motor.begin(I2C_ADDRESS);
}

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...");
        pumpWater();        
    }
}

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");
        Motor.stop(MOTOR2);
        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.

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

In the previous articles, we covered the development of a soil moisture sensing system and the addition of a WiFi module so that the soil moisture information could be sent out. Phase 1 of the project is covered here while Phase 2 (Part 1) is covered here. If you are wondering why there’s no part 2 of Phase 2, it’s taking longer than I anticipated to implement whereas part 1 of Phase 3 is much easier to implement.

And as mentioned in our first article, we will be adding plant watering capabilities in Phase 3 of the project. We will call this the water pumping station.

To do this phase of the project, we will be needing a few additional pieces of hardware to build this station.

Below is the list:

  1. 2x 12v peristaltic pump (also known as Dosing pump)
  2. Grove I2C Motor Driver
  3. Wires – Solid core 22AWG and Stranded (11 to 16 strands)
  4. 12v DC power supply
  5. Stripboard
  6. Soldering kit
  7. 12v barrel connector plugs (5.5mm x 2.1 mm female and male version)

Now that we have the hardware established, let’s get started.

Setting up the power module

This water pumping station will be connected to the existing soil moisture monitoring system. Given that the Arduino Uno accepts a 12v supply through its barrel connector, we could use the same power source as the water pumping station.

To achieve that, we could use a combination of stripboard, wires and barrel connector plugs. Sadly, there were no pictures of how I develop the power module other than the final picture when it’s already installed in the housing. I had to take apart the housing just to take a picture of it.

As you can see, two different type of barrel connector are hooked up and a pair of wires that went to the motor driver board. The female barrel connector is meant to receive the 12v power from the power supply while the male barrel connector plug is meant to connect to the Arduino.

On the other side of the stripboard, the wires are connected in series with no other fancy electrical components. In hindsight, it might have been better if I added a few components such as switches and LED but that would be for some future project.

Implementing the motor driver and adding motors/pumps

Arduino Uno alone can be used to turn on a motor with a combination of:

  1. Digital and analog pins
  2. Electrical components such as resistor, diodes and transistor
  3. External power supply
  4. Breadboard or stripboard

You can even find a tutorial for the above here. What this does is to use the Arduino to generate a signal to control the transistor to run the motor at full speed or turn it off. It does not allow for reversing the motor spin or controlling its speed.

Do not power and drive motors directly from any of the Arduino pins. They do not provide sufficient power to drive motors nor do they have the necessary protection against counter EMF from the motor.

Alternatively, we could go with a motor driver board that comes with all the necessary components installed that will allow us to control multiple motors with ease. It could also allow us to control the speed of the motors and the direction of the speed.

And the second option is what we will go with.

The Grove I2C Motor Driver v1.3 by Seeed is a good choice as it supports driving two motors from its twin channels. The motor driver comes with the L298 IC chip which is a dual full-bridge that support high voltage and current designed to drive inductive load such as relays, solenoids, DC and stepping motors. It also has an ATmega8L chip to enable I2C connection, making it very compatible with Arduino.

We can connect the motor driver to an Arduino using the I2C connection via the Grove port and some jumper wires. But since I don’t have a spare Grove shield, I have to go with the alternative connection. The SCL and SDA pins on the Arduino Uno are A5 and A4 respectively. With that in mind, we can connect the yellow and white wires to those pins. Then connect the 5v and ground pin.

Now we are ready to test the driver with an actual motor or pump.

But first, we will need to connect the wires to the terminals on the motor.

We could also solder the wires to the terminals to ensure a more secure and stable connection. You can see the result below after soldering and the pump is installed in the housing.

After connecting the pump to the motor driver, we could start testing whether the driver works and understand better how to use the hardware.

First, connect the Arduino to the computer. Then upload a BareMinimum sketch to the Arduino before we connect the motor driver. This will remove any existing programs on the Arduino and ensure there is nothing interfering with the driver.

Once the upload is completed, remove the black jumper on the driver. This step is necessary before we power up the driver with the 12v supply as failure to do so will mean 12v going into the Arduino via the 5v pin. Since there’s no electrical protection through that pin, we could fry the Arduino. Once the jumper is removed, we can connect the motor driver to the 12v supply. Alternatively, you could always use a battery to barrel jack adapter such as this and hook up a battery with voltage anywhere between 9v to 12v.

The image below shows the motor driver hooked up to the Arduino with the black jumper removed. The pump is also connected but it’s still in its bubble wrap.

Experimenting with and testing the pump

Based on the specification of the pump, it could do 100ml/min. However, it’s always better to test it and see for yourself the performance of the pump. But before we could do any experimentation, we will attempt to get the motor driver running.

The green LEDs should light up once the driver is powered and running.

But, if you see red LED light up alongside the green, it means that the driver is not initialised properly.

To fix it, simply press the white RESET button on the driver itself while it is connected to a powered Arduino. Everything should work after.

During the various testing session, I noticed that the motor driver always have some initialisation problem after I deploy a Sketch to the Arduino or when it is freshly powered up. That meant I will need to press the RESET button at least once if I want the driver to work properly. This is a problem but we will cover more later as it also affect the housing design. For now, we will continue with figuring out the water flow rate.

To do that, we will need a container filled with water, a measuring beaker and a small medicine cup. After connecting the silicon tubes to the pump, insert the suction tube into the water container.

But how do we know which is the suction end?

For the Gikfun pump that I’m using, the suction end is on the right side if the two tube connectors are facing you. Alternatively, you can determine the suction end by running the pump straight from a 12v supply and check which tube takes in the water. Always ensure the other tube is placed in the measuring beaker. We don’t want to spill water all over the place, especially not when electronics and electricity are involved.

Once we know which end of the pump takes in water and output water, we can start testing how much water is being output.

We can upload a simple Sketch that spins the motor in the pump for a certain amount of time.

#include "Grove_I2C_Motor_Driver.h"

#define I2C_ADDRESS 0x0f

void setup()
{
  Serial.begin(9600);
  Serial.println("Starting motor");

  Motor.begin(I2C_ADDRESS);
  Serial.println("Motor Initialised.");
  delay(2000);
}

void loop() {
 
  Serial.println("Running pump 1");
  Motor.speed(MOTOR1, 100);
  delay(15000); //15 seconds
  Motor.stop(MOTOR1);
  Serial.println("Motor stopped.");
  
  delay(1000UL * 60UL * 5UL);
}

For the test, I went with 15 seconds and ran the pump at 100% for my first experiment. The reason behind for the 15 seconds was based on a gut feel that anything lesser would not be sufficient to water the plant and was kind of pointless to check them out. The 5 minute delay is there to enable us to take measurement of how much water is dumped into the beaker.

Since the smallest value on the beaker was 100ml, there wasn’t sufficient water in the beaker to measure. This is where the medicine cup comes in since the smallest value on it is 1ml and the largest value is 15ml.

I poured the water from the beaker into the medicine cup until it reached the 15 ml mark and checked the beaker. There was no water left in the beaker.

The delay is then increased to 25 seconds and I repeat the measuring process. It turns out the pump put out about 25ml in 25 seconds. This is completely different from what is specified.

If the pump could do 100ml/min, that means it could do 1.6ml per second. At 25 seconds, I should see 41ml of water in 25 seconds and not 25 ml. This just prove that taking actual measurement is always necessary.

With this discovery, I could then determine how long to run the pump for based on the size of the pot, the type of soil and how dry is the soil. This shall be the topic for another article.

Housing the pump station and getting ready for integration

For continuity in design, I will be using Lego bricks again. Previously, I got the Lego Brick Bricks Plate version which comes with 1,500 pieces and some base plates with the intention of doing this project. And I also had left over Lego bricks from my other IoT project that I will be using first.

After choosing the right base plate, the power module was installed first. The bricks were installed with the idea that they should hide the power module since it’s relatively ugly. Once that’s done, the motor driver goes in and the bricks are added.

Below is the initial design that I had before I realised it couldn’t support two pumps.

After some more hours of work, the first pump is now installed and walls went up. The motor driver was moved to the center of the base plate.

This is the side view. As you can see, cable ties are used since the pump has a dimension that’s incompatible with the Lego bricks.

After adding the second pump, the pumping station is now mostly completed.

It was only after the housing is done when I came to the realisation that I need a quick way to access the RESET button as I will be integrating it with the soil moisture monitoring system and will need to deploy new Sketches every now and then. And, I also need to take into account whenever there is a lightning storm, the circuit breaker in my house will trip. In that scenario, the motor driver will probably need a reset.

The good news is there are ISP headers on the board that we can use to perform the reset.

Now, the RST is pulled high by default. To perform the reset, we need to connect it to ground, thereby pulling it low. And we can achieve that with a push button, connecting the RST pin to ground.

So, it was time to take out the motor driver from the housing and solder on the pin headers.

Then, we can prepare a push button and solder the wires on.

Once everything is hooked up, we shall have a quick access reset button.

Then, we will power up the motor driver and test the reset button to make sure it works. As usual, the red LEDs on the driver will light up once powered on. It is because of the driver’s failure to establish the I2C connection. This is when I connected a powered Arduino to the driver and then pressed the reset button. After a while, the red LEDs disappear and that means the I2C connection is established.

It was discovered later that there is a bug with the motor driver’s firmware. It crashes when something that is not hardcoded in the firmware is sent to the driver. This causes the I2C connection to fail. The good news is that a new firmware has since been made available and we could update it by following the instructions here.

Now that I’ve proven the push button to reset works, the housing is then modified to incorporate the button. I had to use several layers of paper to get the button to sit tightly in the window brick.

With that, we can start integrating with the soil moisture monitoring system. The two lego housing are first combined but we won’t be connecting the motor driver or powering it up as we still need to update the sketch program currently running in production.

Here are some picture of the upgraded system.

This marks a good point to stop this article. In the article covering Part 2 Phase 3, we will look at how we setup the tubes around the plant and update the existing sketch program. And let’s not forget that the article for Part 2 Phase 2 is also in the works. In that article, we will look at how to upload telemetry data from the Arduino to a dashboard.