Tutorials
December 1, 2023

Build A Plant Watering Robot with Webhooks And An ESP32

Written by
Nicolas Menard
Staff Engineer

Tending to your houseplants is often an easily neglected task; the occasional vacation or busy work days can leave your leafy companions thirsting for water. Wouldn’t it be great to automate this task? Could we keep it simple and budget friendly?

With Viam, an ESP32, and logic running in the cloud you can build a smart plant watering robot—that only springs to life periodically to tend to your leafy friends—without breaking the bank. This blog post will walk you through building, configuring and scripting a never-failing caretaker for your favorite houseplant(s).

How to setup Viam on an ESP32

ESP32s are low-cost, low-power microcontrollers with integrated Wi-Fi and Bluetooth. While they sound amazing on paper, setting up an environment to program an ESP32 is a tedious and error-prone task. 

Fortunately, when it comes to running Viam on your ESP32, we have simplified the process to a point where you just need to download a binary that will flash your device and configure networking through a couple of steps. You can find a complete guide here.

No need to write code for the ESP32, configuration is done from app.viam.com and using the Viam API will unlock most of the power the ESP32 has to offer. You just need to write your logic using your favorite language with our SDKs, connect to the ESP32, and voila! No more long compile times, hard to debug bugs, and repeated flashing.

Building your plant watering robot

Now that we have an ESP32 running Viam, let’s focus on assembling a practical robot. Our plant watering system is nothing fancy, we are using budget-friendly and readily available components to keep it simple and scalable.

What we used:

  1. ESP32-WROVER-E: This is the brain of the project, slightly more feature-full and recent than most development board which is perfect for our application.
  2. Water Pump: This affordable water pump is perfectly sized for watering plants, it runs on 5V and has a low flow so we don’t over water our plant. A drowned plant is as bad as a dry plant after all.
  3. Relay: The ESP32 pins do not offer enough power to drive a pump. To overcome this limitation, we are using this 5V relay, easily controlled by toggling a pin of the ESP32 high or low. We set it up so that the relay is opened when the control pin is low and closed (so the water pump is running) when the pin is high. This is important because the pin we are using is set low when the ESP32 starts, thus avoiding accidental activation of the pump.
  4. DC to DC converter: Whether you power your robot using a battery or directly plugged into the wall, this converter will supply a stable 5V, with enough power to support multiple pumps or ESP32s.
  5. Moisture sensor: Finally, the moisture sensor is arguably the most important component, detecting when the plant is thirsty, which is, after all, the crux of this problem. We can connect it directly to one of the ADC pins of the ESP32 and read the value; the range of the sensor is 0-3.3V, with 3.3V being extremely dry.
  6. Optionally, you can buy this breakout board for the ESP32; this will make connections easier, especially if you plan to connect multiple pumps/moisture sensors.

The assembled robot:

Wiring to follow for the plant watering robot.

Writing the logic

Time to bring our robot life! Viam supports a variety of programming languages, giving you the flexibility to select the language you’re more comfortable with. We will be choosing Python for its simplicity, readability, and beginner-friendliness.

But before diving into the code we have one more step: configuring the robot. As mentioned before, configuring is done on app.viam.com using JSON, each time the Viam server boots it will query and apply the configuration found on app.viam.com.

In our setup we have connected the relay to pin 19 and the moisture sensor to pin 34, we need to reflect that in the ESP32 board config so that the Viam server configures these pins appropriately.

{
 "components": [
   {
     "attributes": {
      "analogs": [
         {
           "name": "plant1", // the moisture sensor is connected to pin 34
           "pin": "34"
         }
       ],
       "pins": [19]  // the relay logic input is connected to pin 18
     },
     "model": "esp32",
     "name": "board",
     "type": "board"
   }
 ]
}

If you navigate to the Code Sample tab on your robot’s page, you will find code examples and steps to set up the environment for your choice of SDK. The following code is based on the example generated on the same page; you will need to install Viam's Python SDK to run the script locally.

import asyncio

from viam.robot.client import RobotClient
from viam.rpc.dial import Credentials, DialOptions
from viam.components.board import Board
from viam.proto.component.board import PowerMode
from datetime import timedelta
from viam.logging import getLogger
import sys

WATER_FOR_SEC = 20
SLEEP_FOR_MS_LONG = timedelta(hours=1);
SLEEP_FOR_MS_SHORT = timedelta(minutes=1);
MOISTURE_SENSOR_THRESHOLD = 1900
LOGGER = getLogger("plant-watering-robot")

async def connect(robot_url,api_key_id, api_key):
 opts = RobotClient.Options(
  refresh_interval=0,     	 
  check_connection_interval=0,
  attempt_reconnect_interval=0,
  disable_sessions=True,
  dial_options=DialOptions.with_api_key(api_key_id=api_key_id,api_key=api_key)
 )
 return await RobotClient.at_address(robot_url, opts)

async def main():
 robot_url = sys.argv[1]
 api_key_id = sys.argv[2]
 api_key = sys.argv[3]

 LOGGER.info(f"calling the robot with {robot_url} {api_key_id} {api_key}")
 # sometimes the connection can fail, we want to try a couple of time before giving up
 n_try = 10
 while n_try:
  try:
   robot = await connect(robot_url,api_key_id=api_key_id,api_key=api_key)
   break;
  except Exception as e:
   n_try = n_try - 1;
   LOGGER.info("couldn't connect to robot reason {} trying again for {} times".format(e, n_try));    

 # board
 board = Board.from_robot(robot, "board")

 # Get pump pin
 pumpPin = await board.gpio_pin_by_name("19")

 # Set the pump pin to low
 await pumpPin.set(high=False)

 # getting the moisture sensor pin
 analogPin = await board.analog_reader_by_name("plant1")

 # reading its current value, the values returnedx by the ESP32 are pretty stable. If for some
 # reasons you have a noisy environment you could make multiple measurements and average them
 reading = await analogPin.read()

 LOGGER.info(f"moisture level {reading}")

 # by default we would go for a long sleep
 delta = SLEEP_FOR_MS_LONG;

 # if the reading is > 1.9V then we want to water the plant for some time and try again after a short time
 # this would allow the water to diffuse in the plant so that we don't accidentally over-water
 if reading > MOISTURE_SENSOR_THRESHOLD:
   await pumpPin.set(high=True)
   delta = SLEEP_FOR_MS_SHORT


 await asyncio.sleep(delay=WATER_FOR_SEC);

 LOGGER.info(f"Turning pump off")
 await pumpPin.set(high=False)

 LOGGER.info(f"done with watering will powerdown the board for {delta} s")

 try:
   await board.set_power_mode(PowerMode.POWER_MODE_OFFLINE_DEEP, duration=delta, timeout = 5)
 except:
   LOGGER.info("set power mode doesn't return assuming everything went well")

 # the ESP32 is sleeping at this point so we aren't able to gracefully close the connection
 # we therefore quit immediately
 sys.exit(0)

if __name__ == '__main__':
 try:
   asyncio.run(main())
 except Exception as e:
   LOGGER.info(f"closing script after exception {e}")
 sys.exit(0)

As you can see the logic is straightforward. After connecting to the robot, we check whether the soil is wet or dry. If the soil is dry, we will activate the pump for 20 seconds, then tell the robot to shutdown for one minute, after which the script will be executed again. 

Once the appropriate level of moisture is reached, we will tell the robot to enter power-down mode for a day. We leverage the ESP32 deep sleep mode, during which power consumption is lowered to a minimum until an event occurs and the ESP32 wakes up, thus conserving power.

You can test the script using the following command: replace robot_url, api_key_id and api_key with the value found in the code sample table

python main.py robot_url api_key_id api_key

Running it in the cloud

Our plant watering robot is almost done; we could stop here but how do we know that the ESP32 is online and ready to water the plant?

Micro-RDK supports calling a webhook upon startup, so we can just deploy our Python script in the cloud and let it be activated when the server boots.

For this blog post, we have selected fly.io to host our Python script for its simplicity and ease of use. 

  • Clone this repository
  • Replace hook.py with the script above
  • Follow the instructions to deploy your app

We just need to update the configuration on app.viam.com so the server can activate the cloud function when it starts.

 "components": [
   {
     "name": "board",
     "type": "board",
     "attributes": {
       "webhook": "/py",
       "api-key-id": "",
       "api-key": "",
       "analogs": [
         {
           "pin": "34",
           "name": "plant1"
         }
       ],
       "pins": [
         19
       ]
     },
     "depends_on": [],
     "model": "esp32"
   }
 ]

Don’t forget to replace webhook, api-key-id, and api-key with their respective values; the webhook URL is the one returned after you ran flyctl deploy. Don’t forget to add /py at the end of it.

Restart your ESP32, and if everything goes well the cloud function will connect to the Viam server running on the ESP32 and run the logic written in the Python script.

Enhancing your next build

By leveraging the power of the ESP32's low-cost and low-power capabilities, combined with Viam's intuitive platform, you've created a system that's not only efficient but also customizable to your specific needs. So go ahead, give your green friends the attention they deserve, effortlessly!

And why stop there? Take your next build to the next level with our Micro-RDK. Whether you're a seasoned builder or just starting out, our community on Discord is the perfect place to share your progress, get feedback, and connect with fellow enthusiasts. 

We can't wait to see how you harness the power of the Micro-RDK in your innovative projects! 

on this page

Get started with Viam today!