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.

An IoT project: Monitoring soil moisture (Phase 2) – Adding WiFi capability (Part 1)

In phase 1 of the project, we implemented a soil moisture monitoring device that was capable of only telling us the moisture level of the soil through an OLED display and buzzer.

For phase 2, we will be implementing WiFi connectivity for the Arduino so that it could start sending data to a central location. We can then build a dashboard with the data and give us a better picture of the soil status.

To achieve this, we need a WiFi module that will work with the Arduino. There are a few out there in the market but for this home project, we can go with the ESP-01 8266 WiFi module.

We will be splitting the topic on Adding WiFi capability into multiple articles to make it easier to follow through. In this article, we will be discussing the process of understanding and getting the WiFi module ready for the actual implementation.

Setting up the ESP-01 8266 WiFi module

In phase 1, we were using an Arduino Uno for soil moisture monitoring and we will leave it alone since it’s considered running in “production”. Instead, we will be using another Arduino Uno for the development of the WiFi connectivity feature.

We will upload a BareMinimum sketch to the Arduino to get rid of any pre-existing program that is running on the Arduino to test whether the WiFi module is working. Once that is completed, we can start hooking up the ESP-01 to the Arduino.

Let us first disconnect the Arduino from the computer to prevent any accidents such as short-circuiting.

Connecting the WiFi module

Then, we will get a breadboard and some male-male and female-male jumper wires. In my case, I got four male-male and five female-male jumper wires. You can also choose to go with colour-coding the wires so that you know which colour is used for what purpose. We connect the 3.3v and ground from the Arduino to the breadboard’s power rails with the jumper wires. The reason for 3.3V instead of 5V is because the module is designed to run on the former voltage level and has a max tolerance of 3.6V. Running the module on 5V will destroy it.

Next, we connect the Arduino’s TX/RX pins to the breadboard’s vertical rails with another pair of jumper wires. Then, connect the 3V3 and GND from the WiFi module to the power rails on the breadboard with the female-male wires. Following that, the EN (aka CH_PD) is connected to the 3V3 rail. This is the chip enable pin and pulling it high will enable the WiFi module.

This next part is where it gets a little tricky. Getting it wrong could prove fatal for the WiFi module.

This is where colour-coding the wires could prove very useful

Connect the TX pin from the WiFi module to the TX pin from the Arduino on the breadboard. In my case, I use blue wire to indicate this is the data receiving line from the perspective of the Arduino.

The next line to connect is the WiFi module’s RX pin. However, before we can connect it to the Arduino’s RX pin, we need to do something extra. Most of the Arduino digital pin output 5V instead of 3.3V when enabled. This is a problem for the WiFi module since this could destroy the chip. To get around this, we will use a voltage divider.

A voltage divider is a passive linear circuit that produces an output voltage that is a fraction of the input. In its most basic form, we could use two resistors connected in series with the input voltage applied across the resistor pair. The desired output voltage emerges from the connection between the resistor pair.

The output voltage is determined by the following formula:

Vout= (Vin x R2) / (R1   R2) =>
Where:
 R1 and R2 is the resistance value in Ohm
 Vin is the input voltage
 Vout is the output voltage

I used this website to help me make a quick determination of the resistance values I need.

For R1, I used 1.2 kiloohm (Resistor Colour: Brown, Red, Black, Brown, Brown) while R2 is 1.8 kiloohm (Resistor Colour: Brown, Grey, Black, Brown, Brown). This combination gives us a Vout of 3V which is more than sufficient for our purpose.

On the breadboard, R1 connects the Arduino’s RX pin to an empty vertical rail and R2 connects R1 to Ground. Then, we connect the WiFi module’s RX pin to the connection between the resistor pair. I use orange wires for the RX pins.

The final result looks like this:

Once the WiFi module is connected, we can power up the Arduino by connecting it to the computer over USB.

Testing the WiFi module

Once the Arduino is turned on, open the Arduino IDE (if you haven’t already) and then, open the Serial Monitor.

By default, the WiFi module comes with the AT firmware. The documentation for the available commands can be found here.

You can choose to use other type of firmware like the NodeMCU but for the project, we won’t be using that.

For the Serial Monitor, ensure the Baud rate is set to 1152000 and the dropdown option Both NL & CR is selected as WiFi AT firmware depends on the newline and carriage return to determine if a message is ready.

In the textfield, type “AT” and press Enter. This will send the AT command to the WiFi module. It should return an “Ok” response.

Note: All AT commands have to be uppercase.

Next, let’s check the version of the firmware running on the WiFi module by typing the command: AT GMR?

From the response, it appears that the WiFi module has a really old version of the firmware. We will need to update it to use newer commands.

If we go to the download section for the module on ExpressIf website, we can find the latest version of the AT firmware. However, that may not be the best firmware version for us to use at least according to the information found in this forum topic. Another user had attempted to flash the firmware (ESP8266 IDF AT Bin V2.0) to the latest as of the topic post but the WiFi module stopped responding to AT commands. Instead, v1.74 is the better version to use.

Since I’m using a Mac, I can’t run the default Flash Download Tool from ExpressIf website. It is for Windows only. What we can do is to use a python-based firmware flashing application call esptool. It was originally created by Fredik Ahlberg as a an unofficial community project and ExpressIf has since supported it.

We can install the esptool via pip command according to the README found here. After that, we will download the AT v1.74 firmware from here.

Unzip it to a folder of your choice and open Terminal on the Mac. Navigate to the folder containing the unzipped firmware and ensure you are in the ‘bin’ folder. For me, I’ve placed it in my Downloads folder.

Now, we are ready to start flashing the firmware.

Flashing ESP-01 8266 Firmware

Before the firmware flashing process, we need to do two things:

  1. Determine the port of the WiFi module.
  2. Get the module into a programmable state.

To determine the port, we can use the Arduino IDE. Navigate to Tools menu and under Ports, we should see the port the Arduino is connected to.

Take note of the port and then we can proceed to get the WiFi module into a programmable state.

The steps are as follows:

  1. Connect the RESET pin on the Arduino to Ground with a jumper wire.
  2. Connect the WiFi module’s GPIO_0 pin to Ground on the breadboard. Keep this pin connected throughout the whole flashing process.
  3. Connect the WiFi module’s RESET pin to Ground on the breadboard for exactly one second and disconnect it.

The WiFi module should now be ready for programming.

From the Terminal, type the following command. The value for the –port parameter should be the same port you determined earlier. Then, press Enter to start the flashing process.

esptool.py --port /dev/cu.usbmodem1432101 write_flash --flash_mode dout --flash_size 1MB 0x0 boot_v1.7.bin 0x01000 at/512 512/user1.1024.new.2.bin 0xfb000 blank.bin 0xfc000 esp_init_data_default_v08.bin 0xfe000 blank.bin 0x7e000 blank.bin

You should see the following during the flashing process.

After the flashing is done, let’s check the WiFi module to ensure it’s still working.

We will send the “AT” command and we should get an “Ok” response. After that, we send the AT GMR? command. We should get the following response:

Also, the baud rate by default runs at 115200 and that’s too fast. We will need to slow it down to 9600. To do so, we will need to use the AT UART_DEF command. Information about the command can be found here.

To set the baud rate, type the command AT UART_DEF=9600,8,1,0,0 and then press enter.

After running the command, change the baud rate on the Serial Monitor to 9600.

Then, run the AT UART_DEF? command.

The above looks good. The WiFi module is now updated and the baud rate is set. We are ready to go to the next stage.

Scanning for WiFi

In order for the program on the Arduino to communicate with the WiFi module, we will need to use another set of pins. By default, the TX and RX pins on the Arduino is used for Serial communication (e.g. USB). These are the pins that allow us to upload Sketch to the device and also used for the Serial Monitor. When other devices are connected to the Arduino via the TX and RX pins, we are using the Arduino as a USB to TTL serial adapter, which could interfere with the communication with the WiFi module in our case.

To use other Digital Pins on the Arduino for Serial Communication, we can use the SoftwareSerial library, which will emulate the remaining digital pins.

First, let’s disconnect the WiFi’s TX and RX pins from the Arduino. Then, create a new Sketch and define the following.

#include "SoftwareSerial.h"

const byte rxPin = 2;
const byte txPin = 3;

SoftwareSerial wifiSerial(2,3); //PIN 2 to receive data from WiFi, PIN 3 to transmit data to WiFi module

The above code is setting Pin 2 as the RX Pin and Pin 3 as the TX Pin from the perspective of the Arduino. What that means is that Pin 2 will be used to receive data from the WiFi module whereas Pin 3 will be used to send data to the WiFi module for transmission to the outside world.

Then, in the setup function of the Sketch, we will establish the USB serial connection to have a baud rate of 115200. After that, set the pinMode for the rxPin and txPin to INPUT and OUTPUT respectively. Then, we also initialise the SoftwareSerial library to run at 9600 baud.

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);

  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
  
  wifiSerial.begin(9600);
}

Now, we are ready to connect the WiFi module to the Arduino. Unlike the earlier connection we did, the TX and RX pins are swapped around. We will connect the RX pin from the WiFi module to Pin 3 while the TX pin will go to Pin 2. As mentioned before, we have to be careful not to insert the WiFi TX pin to the Arduino Pin 3 since that pin will now be outputting 5V once the above Sketch is deployed to the Arduino.

To check that the WiFi module is working well with the SoftwareSerial, we will use it to query for available access points.

For this, I tried to send the AT CWLAP command via println function. Then, I will read the input. After several tries, I realised it’s actually very tedious and error-prone to work with serial communication.

I needed a better solution.

During my research, I found that the easiest library to use is the WiFiEsp. This library that can be found via the library manager of the Arduino IDE. After installing the latest version, we will proceed with implementing the WiFi access point scanning capability.

First, let’s modify the start of the Sketch to use the WiFiEsp library.

#include "WiFiEsp.h"

const byte rxPin = 2;
const byte txPin = 3;

#ifndef HAVE_HWSERIAL1
    #include "SoftwareSerial.h"
    SoftwareSerial wifiSerial(rxPin, txPin); //PIN 2 to receive data from WiFi, PIN 3 to transmit data to WiFi module
#endif

Then, modify the setup function as follows:


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);

  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);

  wifiSerial.begin(9600);
  WiFi.init(&wifiSerial);

}

Next, in the loop function, we do the following:

  Serial.println();
  Serial.println("Scanning available networks ... ");

  printNetworkScanResult();
  delay(10000);

For the printNetworkScanResult function, we do the following:


void printNetworkScanResult()
{
  int numSsid = WiFi.scanNetworks();
  while (numSsid == -1)
  {
    Serial.println("Couldn't get a WiFi connection");
    delay(3000);
    numSsid = WiFi.scanNetworks();
  }


  Serial.print("Number of available networks: ");
  Serial.println(numSsid);

  for (int i = 0; i < numSsid; i  )
  {
    Serial.print(i   1);
    Serial.print(")");
    Serial.print(WiFi.SSID(i));
    Serial.println();
  }
}

Once the sketch is deployed to the Arduino and it started running, look at the response returned in the Serial monitor. We should see a list of WiFi access point.

Finally, now we know that the WiFi module is working.

And, this is a good point to end this article before I run the risk of droning on. In the next article, we will look at implementing the actual WiFi communication and integrating it with our existing soil moisture monitoring program.