Plant Watering Robot with a Raspberry Pi

Picture of the plant watering robot on a desk.

Building a useful robot doesn’t have to require complicated code or expensive equipment. With a Raspberry Pi and some cheap, basic hardware, you can keep your plants healthy and happy from anywhere in the world!

Follow this tutorial to learn how to set up an automatic plant watering system:

  1. Complete the physical assembly and wiring.
  2. Create and connect to the robot, and configure your robot’s components.
  3. Configure the ADC as a module from the registry.
  4. Write code utilizing the Viam Python SDK to control the plant watering robot.

You can also follow a simplified version of this tutorial in this video: it eliminates the need for the ADC and the breadboard, instead using the digital pin of the moisture sensor to get “high” and “low” readings and to turn a relay on and off. You can start with this simple version and then add the ADC to make your machine more accurate!

The tutorial uses the following hardware, but you can adjust it as needed:

Before starting this tutorial, follow the Raspberry Pi Setup Guide to prepare your Pi to run viam-server. Connect your Pi to its power supply to power it on. Make sure your Pi is flashed with a Viam-compatible operating system, and that you are able to SSH into it.

Set up your plant watering robot

Before programming the Pi to make the plant watering robot functional, you need to physically set up the plant watering robot by wiring the different components together. You will set up the robot to receive signals from the resistive soil moisture sensor and signal to the pump when it is time to pump water from the water’s container to the plant’s container.

Full wiring diagram

Refer back to this diagram as you complete the steps to wire your hardware.

The full wiring diagram for all the hardware for the Plant Watering Robot.

Wire your ADC

The analog-to-digital converter (ADC) converts the resistive soil moisture sensor’s analog readings to digital signals that can be processed by your Pi, which expects digital signals to come to it through its GPIO pins.

Start by wiring your ADC to your Raspberry Pi board.

You can find a Raspberry Pi pinout diagram at pinout.xyz. Reference the following pinout diagram for your MCP3008 analog-to-digital converter:

Pinout diagram for the ADC.

Insert the MCP3008 into your breadboard so that it bridges both sides of the divide. Then, use the rows on the side of your MCP3008’s pins and the GPIO pins on your Pi to connect the pins with wires as follows:

MCP3008 ADC PinRaspberry Pi Pin
VDD5V
VREF5V
AGNDGND
DGNDGND
CLKSCLK
DOUTMISO
DINMOSI
CS/SHDN24GPIO8

Use an additional wire to wire Raspberry Pi pin 4 (a 5 volt power pin) to the power rail of the breadboard (the red plus sign column). This brings the 5V power output from the Raspberry Pi to the ADC and the sensor.

Wire your resistive soil moisture sensor

Next, wire your resistive soil moisture sensor to your Pi and ADC.

Reference this diagram of the blue module part of the sensor:

Pinout diagram for the resistive soil moisture sensor.

Start by connecting the female jumper wires at the end of the sensor prongs to the blue module where the diagram shown above is labeled “Connect with Probe.” Be careful of the positive and negative sides, and make sure to match them correctly.

Then, wire the rest of the pins on the module to the Pi and ADC as follows:

PiADC
Moisture Sensor PinRaspberry Pi Pin
VCC5V on the power rail
GNDGND
Moisture Sensor PinMCP3008 ADC Pin
A0 (Analog Signal Output)CH0

Put the soil moisture sensor inside of the container holding your plant.

Wire your pump

Now, wire and power your pump motor and relay module to complete your hardware setup:

  1. Attach an alligator wire clip to your 5V pump motor’s positive wire, and connect it to the NO pin on relay module. NO stands for normally open, which will keep the circuit open unless the pin is triggered.
  2. Attach another alligator wire clip to your 5V pump motor’s negative wire, and connect it to pin 39 (ground) on the Raspberry Pi.
  3. Connect the COM (common) pin on the relay to pin 1 (3.3V) on the Pi.
  4. Connect the 5V pin on the relay to pin 2 (5V) on the Pi.
  5. Connect the GND pin on the relay to pin 14 (ground) on the Pi.
  6. Connect the IN pin on the relay to the pin 8 (GPIO 14) on the Pi.

Program your plant watering robot

Enable SPI on your Pi

Now that you have wired your ADC and moisture sensor, make sure that the Serial Peripheral Interface (SPI) is enabled on your Pi. Enabling this protocol is necessary to allow the Pi to communicate with the moisture sensor peripheral.

SSH into your Pi and run the following command:

sudo raspi-config

Once the raspi-config interface is open, navigate to Interface Options:

Raspi-config Tool interface with Interface Options selected.

Then, select SPI:

Raspi-config Tool interface with SPI selected.

Now, select Yes to enable SPI:

Raspi-config Tool interface with Yes selected for SPI enablement.

Finally, select Finish. Restart your Pi using sudo reboot to make these changes take effect.

Test your soil moisture readings on your Pi

Next, install the Adafruit ADC library Adafruit_CircuitPython_MCP3xxx on your Pi.

Before installation, make sure any packages on your Pi are up to date:

sudo apt update
sudo apt upgrade

Make sure you have pip installed for Python 3:

pip --version

If not, run the following command:

sudo apt install python3-pip

Run the following command while connected to your Pi with SSH to install Adafruit_CircuitPython_MCP3xxx:

sudo pip3 install adafruit-circuitpython-mcp3xxx

Create a new directory for your plant watering robot’s files and navigate to this directory in your terminal session. For example, run the following commands:

mkdir plant-watering-robot
cd plant-watering-robot

After navigating to this directory, create a new Python file called adctesting.py and open up the file. For example, run the following commands:

touch adctesting.py
nano adctesting.py

Now, add the following Python code to adctesting.py to test reading values from your resistive soil moisture sensor through your MCP3008 ADC:

import time
import busio
import digitalio
import board
import adafruit_mcp3xxx.mcp3008 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn

# Create the SPI bus
spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)

# Create the cs (chip select)
cs = digitalio.DigitalInOut(board.D8)

# Create the MCP3008 object
mcp = MCP.MCP3008(spi, cs)

# Create an analog input channel on Pin 0
chan = AnalogIn(mcp, MCP.P0)

print('Reading MCP3008 values, press Ctrl-C to quit...')
while True:
    print('Raw ADC Value: ', chan.value)
    time.sleep(1)

Run the code as follows:

sudo python3 adctesting.py

Now, you should see the moisture sensor values outputted by the MCP3008.

Test your sensor by putting it in air, water, and different soils to see how the values change to determine your baseline for wet and dry values.

Terminal output of resistive soil moisture sensor values.

Configure the components of your robot in the Viam app

Add a new machine in the Viam app. Then follow the setup instructions to install viam-server on the computer you’re using for your project and connect to the Viam app. Wait until your machine has successfully connected.

Then, navigate to your new machine’s page in the app and click on the Config tab.

First, add your Pi as a board component by creating a new component with type board and model pi:

Creation of a pi board in the Viam app config builder.

{
  "components": [
    {
      "name": "local",
      "model": "pi",
      "type": "board",
      "namespace": "rdk",
      "attributes": {},
      "depends_on": []
    } // Motor JSON ...
  ]
}

Then, add your pump as a motor component by adding a new component with type motor and model gpio.

Set the motor’s attributes Max RPM to 1000 and PWM to 8 GPIO 14 (the board and GPIO pin that you wired the relay’s IN to).

Creation of a pump motor in the Viam app config builder.

// Board JSON ... },
{
  "name": "water-pump",
  "model": "gpio",
  "type": "motor",
  "namespace": "rdk",
  "attributes": {
    "pins": {
      "a": "",
      "b": "",
      "dir": "",
      "pwm": "8"
    },
    "board": "local",
    "max_rpm": 1000
  },
  "depends_on": []
}

Click Save config.

Now, if you navigate to your machine’s Control tab, you should be able to control the motor by setting the power and direction of rotation and clicking the RUN button:

Creation of a pump motor in the Viam app config builder.

Configure the ADC as a module from the registry

Resources refer to the different components and services Viam provides for robots to use. Components refer to types of hardware, and each component’s built-in models support the most common models of this hardware. For example, the sensor component has an ultrasonic model built in for the ubiquitous ultrasonic sensor.

However, there are many different types of sensors used for sensing different things across the Internet of Things. Although the resistive soil moisture sensor is not currently one of Viam’s built-in models, you can add an analog-to-digital-converter (ADC) as a module and use it to get readings from the moisture sensor.

A module provides one or more modular resources, which add resource types (components and services) or models that are not built into Viam. A module can be added to your robot from the Viam registry.

The Viam registry allows hardware and software engineers to collaborate on their robotics projects by writing and sharing custom modules with each other. You can add a module from the Viam registry directly from your robot’s Configuration tab in the Viam app, using the + Create component button.

Add the mcp300x-adc-sensor module to your robot in 3 steps:

  1. Go to your machine’s Config tab. Select Create component.

  2. Search mcp300x. Click Add module.

  3. Give your module a name of your choice, and click Create to add this module to your machine.

  4. Find your module’s card on the Config page. In the attributes field, add the necessary attributes as "channel_map" and "sensor_pin". For example, if you have a moisture sensor on channel 0, and your sensor_pin is 8, your configuration should look like this:

    {
      "channel_map": {
        "moisture": 0
      },
      "sensor_pin": 8
    }
    

Save your config.

This module allows you to get multiple readings at the same time from different channels of the ADC sensor. If you wire and configure another sensor, such as a temperature sensor on channel 1, you can add the sensor to the "channel_map" and get a reading from it.

Now that you have set up your robot and are able to control your motor, you can put the suction tube of your pump into the water cup, and the output tube into the plant!

Add Python control code

Follow these instructions to start working on your Python control code:

  1. Navigate to your machine’s page in the Viam app, and click on the Code sample tab.

  2. Select Python as the language.

  3. Click Copy to copy the generated code sample, which establishes a connection with your robot when run.

  4. Paste this code sample into a new file in the plant-watering-robot directory you created on your Pi.

  5. Name the file plant-watering-robot.py, and save it.

For example, run the following commands on your Pi to create and open the file:

cd plant-watering-robot
touch plant-watering-robot.py
nano plant-watering-robot.py

Now, you can add code into plant-watering-robot.py to write the logic that defines your plant watering system.

To start, add your system logic code into the main() function of the program. Use the Viam motor and sensor API methods.

You can get your components from the robot like this:

# Note that this name, `sensor`, is defined when you add the module
sensor = Sensor.from_robot(robot=robot, name='sensor')
water_pump = Motor.from_robot(robot=robot, name='water-pump')

And you can add your system logic to run continuously like this:

while True:

    # Get the moisture sensor's readings
    readings = await sensor.get_readings()
    soil_moisture = readings.get('moisture')

    # Calculate average moisture reading from the list of readings, to account
    # for outliers
    avg_moisture = sum(soil_moisture) / len(soil_moisture)

    # If the average moisture reading is greater than 60000, trigger pump
    # watering
    if (avg_moisture > 60000):
        print('this plant is too thirsty! giving it more water')

        # Run the water pump for 100 rev. at 1000 rpm
        await water_pump.go_for(rpm=1000, revolutions=100)

        # Wait 60 seconds so that the water can soak into the soil a bit before
        # trying to water again
        print('waiting a little bit for water to soak in')
        time.sleep(60)

See the motor component’s API documentation for more information about water_pump.go_for().

Save your plant-watering-robot.py program with this logic added in, and then run it on your Pi like this:

sudo python3 plant-watering-robot.py

To tinker this example code to work best for you, determine at what analog value from the soil moisture readings you want to water your plant, as your thirsty plant’s average moisture reading might differ from our example value of 60000. Also, consider how often you would like to check the moisture levels of the plant, and how long the plant should be watered.

Next steps

Now that you have created your automatic plant watering system with a resistive soil moisture sensor, you can easily use Viam to automate other aspects of your garden. For example, you can use a light sensor or a temperature sensor, and get readings from other channels of the MCP3008! If you build something based on this please share it in our Community Discord - we’d love to see it.