# Build a plant watering robot with webhooks and an ESP32

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? 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

[ESP32](https://en.wikipedia.org/wiki/ESP32) s 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 can be tedious and error-prone.

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](https://docs.viam.com/installation/prepare/microcontrollers/).

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](https://docs.viam.com/program/), 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](https://www.amazon.com/Espressif-ESP32-DevKitC-VE-Development-Board/dp/B087TNPQCV/ref=sr_1_7?crid=1D1GD3O20R713&keywords=WROVER-E&qid=1701359741&s=electronics&sprefix=wrover-e%2Celectronics%2C69&sr=1-7&th=1)**: This is the brain of the project, slightly more feature-full and recent than most development boards which is perfect for our application.
2. **[Water Pump](https://www.amazon.com/MECCANIXITY-Water-Submersible-Flower-Gardening/dp/B09TGK9N5Q/ref=sr_1_4?crid=3AUDF91FRJ4JW&keywords=water+pump+plant+gardening+5V&qid=1700655966&s=hi&sprefix=water+pump+plant+gardening+5v%2Ctools%2C66&sr=1-4)**: This affordable water pump is perfectly sized for watering plants, it runs on 5V and has a low flow so we don’t overwater our plant.
3. **[Relay](https://www.amazon.com/HiLetgo-Channel-optocoupler-Support-Trigger/dp/B00LW15A4W/ref=sr_1_4?keywords=5V+Relay&qid=1700656080&sr=8-4)**: The ESP32 pins do not offer enough power to drive a pump. To overcome this limitation, we are using a 5V relay.
4. **[DC to DC converter](https://www.amazon.com/gp/product/B01NALDSJ0/ref=ewc_pr_img_1?smid=AFHAE9RJVUMB&psc=1)**: This converter will supply a stable 5V, with enough power to support multiple pumps or ESP32s.
5. **[Moisture sensor](https://www.amazon.com/Capacitive-Moisture-Corrosion-Resistant-Detection/dp/B07SYBSHGX/ref=sxts_b2b_sx_reorder_acb_customer?content-id=amzn1.sym.44ecadb3-1930-4ae5-8e7f-c0670e7d86ce%3Aamzn1.sym.44ecadb3-1930-4ae5-8e7f-c0670e7d86ce&crid=Y31XMP4HT7XB&cv_ct_cx=moisture+sensor&keywords=moisture+sensor&pd_rd_i=B07SYBSHGX&pd_rd_r=5d04cd4e-4cc5-4c57-ac39-afd3cefa622c&pd_rd_w=X95dM&pd_rd_wg=WRJ0F&pf_rd_p=44ecadb3-1930-4ae5-8e7f-c0670e7d86ce&pf_rd_r=8PFANF06XPZJPY9TYXH3&qid=1700656124&sbo=RZvfv%2F%2FHxDF%2BO5021pAnSA%3D%3D&sprefix=moisture+sensor%2Caps%2C75&sr=1-1-62d64017-76a9-4f2a-8002-d7ec97456eea)**: The moisture sensor is arguably the most important component, detecting when the plant is thirsty, which is, after all, the crux of this problem.
6. Optionally, you can buy this **[breakout board](https://www.amazon.com/gp/product/B09VMXQTM8/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1)** for the ESP32; this will make connections easier.

### The assembled robot:

_Wiring to follow for the plant watering robot._

### Writing the logic

Time to bring our robot to life! Viam supports a variety of programming languages. 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. Configuration is done on [app.viam.com](http://app.viam.com/) using JSON, each time the Viam server boots it will query and apply the configuration found there.

```json
{
 "components": [
   {
     "attributes": {
      "analogs": [
         {
           "name": "plant1",
           "pin": "34"
         }
       ],
       "pins": [19]
     },
     "model": "esp32",
     "name": "board",
     "type": "board"
   }
 ]
}
```

If you navigate to the Code Sample tab on your robot’s page, you will find code examples for setting 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](https://python.viam.dev/) to run the script locally.

```python
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}")
 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
 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
 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")
 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 shut down for one minute. Once the appropriate level of moisture is reached, we will tell the robot to enter power-down mode for a day.

### Running it in the cloud

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

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

1. Clone [this](https://github.com/viam-labs/webhook-template) repository
2. Replace hook.py with the script above
3. 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.

```json
{
 "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**. 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.
