# Discord and Slack Connected Smart Plant with Adafruit IO Actions

## Overview

![](https://cdn-learn.adafruit.com/assets/assets/000/102/429/medium800thumb/adafruit_io_ezgif.com-gif-maker_%281%29.jpg?1622147206)

_Always on the computer with no time to water your plants?_ Build an internet-enabled planter to send notifications over a [Slack](https://slack.com/) or [Discord](https://discord.com/) channel when it needs water.

In this project, you'll build an IoT planter using the Raspberry Pi Pico RP2040 and the STEMMA Soil Sensor. Then, you'll add an AirLift breakout board to connect it to the internet and send sensor data to [Adafruit IO](http://io.adafruit.com/). Finally, you'll use Adafruit IO's reactive action features to send webhook notifications about your planter to a Discord or Slack server.

For this project, you can use Slack **or** Discord. While it is possible to do both, this guide won't go into that.

## Parts
### Adafruit STEMMA Soil Sensor - I2C Capacitive Moisture Sensor

[Adafruit STEMMA Soil Sensor - I2C Capacitive Moisture Sensor](https://www.adafruit.com/product/4026)
Most low cost soil sensors are _resistive_ style, where there's two prongs and the sensor measures the conductivity between the two. These work OK at first, but eventually start to oxidize because of the exposed metal. Even if they're gold plated! The resistivity measurement...

Out of Stock
[Buy Now](https://www.adafruit.com/product/4026)
[Related Guides to the Product](https://learn.adafruit.com/products/4026/guides)
![Demo Shot of the Adafruit STEMMA Soil Sensor - I2C Capacitive Moisture Sensor in a small potted plant, with wires connecting it to an Adafruit Metro.](https://cdn-shop.adafruit.com/640x480/4026-01.jpg)

### Adafruit Feather RP2040

[Adafruit Feather RP2040](https://www.adafruit.com/product/4884)
A new chip means a new Feather, and the Raspberry Pi RP2040 is no exception. When we saw this chip we thought "this chip is going to be awesome when we give it the Feather Treatment" and so we did! This Feather features the&nbsp; **RP2040** , and all niceties you know and...

In Stock
[Buy Now](https://www.adafruit.com/product/4884)
[Related Guides to the Product](https://learn.adafruit.com/products/4884/guides)
![Angled shot of black rectangular microcontroller "Feather RP2040"](https://cdn-shop.adafruit.com/640x480/4884-04.jpg)

### Adafruit AirLift FeatherWing – ESP32 WiFi Co-Processor

[Adafruit AirLift FeatherWing – ESP32 WiFi Co-Processor](https://www.adafruit.com/product/4264)
Give your Feather project a _lift_ with the Adafruit AirLift FeatherWing - a FeatherWing that lets you use the powerful ESP32 as a WiFi co-processor. You probably have your favorite Feather ([like the Feather M4](https://www.adafruit.com/product/3857)) that comes with its own...

In Stock
[Buy Now](https://www.adafruit.com/product/4264)
[Related Guides to the Product](https://learn.adafruit.com/products/4264/guides)
![Angled shot of Adafruit AirLift FeatherWing.](https://cdn-shop.adafruit.com/640x480/4264-07.jpg)

### FeatherWing Doubler - Prototyping Add-on For All Feather Boards

[FeatherWing Doubler - Prototyping Add-on For All Feather Boards](https://www.adafruit.com/product/2890)
This is the **FeatherWing Doubler** - a prototyping add-on and more for all Feather boards. This is similar to our [FeatherWing Proto](https://www.adafruit.com/products/2884) except there are two! The magic of the Doubler comes when stacking a Feather and another...

In Stock
[Buy Now](https://www.adafruit.com/product/2890)
[Related Guides to the Product](https://learn.adafruit.com/products/2890/guides)
![Double prototyping feather wing PCB with socket headers installed](https://cdn-shop.adafruit.com/640x480/2890-01.jpg)

### 4-pin JST PH to JST SH Cable - STEMMA to QT / Qwiic

[4-pin JST PH to JST SH Cable - STEMMA to QT / Qwiic](https://www.adafruit.com/product/4424)
Are you a maker in the midst of&nbsp;a [**STEMMA**](https://learn.adafruit.com/introducing-adafruit-stemma-qt/what-is-stemma) dilemma? This 200mm long 4-wire cable is a fantastic chimera-cable fitted with **STEMMA QT / Sparkfun Qwiic JST SH** on one end,...

In Stock
[Buy Now](https://www.adafruit.com/product/4424)
[Related Guides to the Product](https://learn.adafruit.com/products/4424/guides)
![Angled shot of 4-pin JST PH to JST SH Cable. ](https://cdn-shop.adafruit.com/640x480/4424-02.jpg)

### USB Type A to Type C Cable - approx 1 meter / 3 ft long

[USB Type A to Type C Cable - approx 1 meter / 3 ft long](https://www.adafruit.com/product/4474)
As technology changes and adapts, so does Adafruit. This&nbsp;&nbsp; **USB Type A to Type C** cable will help you with the transition to USB C, even if you're still totin' around a USB Type A hub, computer or laptop.

USB C is the latest industry-standard connector for...

In Stock
[Buy Now](https://www.adafruit.com/product/4474)
[Related Guides to the Product](https://learn.adafruit.com/products/4474/guides)
![Angled shot of a coiled black, USB-C to USB-A cable.](https://cdn-shop.adafruit.com/640x480/4474-02.jpg)

# Discord and Slack Connected Smart Plant with Adafruit IO Actions

## Install CircuitPython

[CircuitPython](https://github.com/adafruit/circuitpython) is a derivative of [MicroPython](https://micropython.org) designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the **CIRCUITPY** drive to iterate.

## CircuitPython Quickstart

Follow this step-by-step to quickly get CircuitPython running on your board.

[Download the latest version of CircuitPython for this board via circuitpython.org](https://circuitpython.org/board/adafruit_feather_rp2040/)
 **Click the link above to download the latest CircuitPython UF2 file.**

Save it wherever is convenient for you.

![install_circuitpython_on_rp2040_RP2040_UF2_downloaded.jpg](https://cdn-learn.adafruit.com/assets/assets/000/101/655/medium640/install_circuitpython_on_rp2040_RP2040_UF2_downloaded.jpg?1618943202)

![](https://cdn-learn.adafruit.com/assets/assets/000/102/705/medium800/adafruit_products_FeatherRP_buttons_highlighted.jpg?1623167565)

To enter the bootloader, hold down the **BOOT/**** BOOTSEL button**(highlighted in red above), and while continuing to hold it (don't let go!), press and release the**reset button**(highlighted in red or blue above).&nbsp;**Continue to hold the BOOT/BOOTSEL button until the RPI-RP2 drive appears!**

If the drive does not appear, release all the buttons, and then repeat the process above.

You can also start with your board unplugged from USB, press and hold the BOOTSEL button (highlighted in red above), continue to hold it while plugging it into USB, and wait for the drive to appear before releasing the button.

A lot of people end up using charge-only USB cables and it is very frustrating! **Make sure you have a USB cable you know is good for data sync.**

You will see a new disk drive appear called **RPI-RP2**.

&nbsp;

Drag the **adafruit\_circuitpython\_etc.uf2** file to **RPI-RP2.**

![install_circuitpython_on_rp2040_RP2040_bootloader_drive.jpg](https://cdn-learn.adafruit.com/assets/assets/000/101/656/medium640/install_circuitpython_on_rp2040_RP2040_bootloader_drive.jpg?1618943666)

![install_circuitpython_on_rp2040_RP2040_drag_UF2.jpg](https://cdn-learn.adafruit.com/assets/assets/000/101/657/medium640/install_circuitpython_on_rp2040_RP2040_drag_UF2.jpg?1618943674)

The **RPI-RP2** drive will disappear and a new disk drive called **CIRCUITPY** will appear.

That's it, you're done! :)

![install_circuitpython_on_rp2040_RP2040_CIRCUITPY.jpg](https://cdn-learn.adafruit.com/assets/assets/000/101/658/medium640/install_circuitpython_on_rp2040_RP2040_CIRCUITPY.jpg?1618943864)

## Safe Mode

You want to edit your **code.py** or modify the files on your **CIRCUITPY** drive, but find that you can't. Perhaps your board has gotten into a state where **CIRCUITPY** is read-only. You may have turned off the **CIRCUITPY** drive altogether. Whatever the reason, safe mode can help.

Safe mode in CircuitPython does not run any user code on startup, and disables auto-reload. This means a few things. First, safe mode _bypasses any code in_ **boot.py** (where you can set **CIRCUITPY** read-only or turn it off completely). Second, _it does not run the code in_ **code.py**. And finally, _it does not automatically soft-reload when data is written to the_ **CIRCUITPY** _drive_.

Therefore, whatever you may have done to put your board in a non-interactive state, safe mode gives you the opportunity to correct it without losing all of the data on the **CIRCUITPY** drive.

### Entering Safe Mode
To enter safe mode when using CircuitPython, plug in your board or hit reset (highlighted in red above). Immediately after the board starts up or resets, it waits 1000ms. On some boards, the onboard status LED (highlighted in green above) will blink yellow during that time. If you press reset during that 1000ms, the board will start up in safe mode. It can be difficult to react to the yellow LED, so you may want to think of it simply as a slow double click of the reset button. (Remember, a fast double click of reset enters the bootloader.)

### In Safe Mode

If you successfully enter safe mode on CircuitPython, the LED will intermittently blink yellow three times.

If you connect to the serial console, you'll find the following message.

```terminal
Auto-reload is off.
Running in safe mode! Not running saved code.

CircuitPython is in safe mode because you pressed the reset button during boot. Press again to exit safe mode.

Press any key to enter the REPL. Use CTRL-D to reload.
```

You can now edit the contents of the **CIRCUITPY** drive. Remember, _your code will not run until you press the reset button, or unplug and plug in your board, to get out of safe mode._

## Flash Resetting UF2

If your board ever gets into a really _weird_ state and CIRCUITPY doesn't show up as a disk drive after installing CircuitPython, try loading this 'nuke' UF2 to RPI-RP2. which will do a 'deep clean' on your Flash Memory. **You will lose all the files on the board** , but at least you'll be able to revive it! After loading this UF2, follow the steps above to re-install CircuitPython.

[Download flash erasing "nuke" UF2](https://cdn-learn.adafruit.com/assets/assets/000/101/659/original/flash_nuke.uf2?1618945856)
# Discord and Slack Connected Smart Plant with Adafruit IO Actions

## Internet Connect!

# Connect to WiFi

OK, now that you have your&nbsp; **settings.toml** file set up - you can connect to the Internet.

To do this, you need to first install a few libraries, into the lib folder on your **CIRCUITPY** drive. Then you need to update **code.py** with the example script.

Thankfully, we can do this in one go. In the example below, click the **Download Project Bundle** button below to download the necessary libraries and the **code.py** file in a zip file. Extract the contents of the zip file, open the directory **examples/** and then click on the directory that matches the version of CircuitPython you're using and copy the contents of that directory to your **CIRCUITPY** drive.

Your **CIRCUITPY** drive should now look similar to the following image:

![CIRCUITPY](https://adafruit.github.io/Adafruit_CircuitPython_Bundle/esp32spi_esp32spi_simpletest.py.png )

Info: Update to CircuitPython 9.2.x or later to use this example.

https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI/blob/main/examples/esp32spi_simpletest.py

And save it to your board, with the name **code.py**.

Don't forget you'll also need to create the **settings.toml** file as seen above, with your WiFi ssid and password.

In a serial console, you should see something like the following. For more information about connecting with a serial console, view the guide [Connecting to the Serial Console](https://learn.adafruit.com/welcome-to-circuitpython/kattni-connecting-to-the-serial-console).

```terminal
&gt;&gt;&gt; import wifitest
ESP32 SPI webclient test
ESP32 found and in idle mode
Firmware vers. 1.7.5
MAC addr: 24:C9:DC:BD:0F:3F
	HomeNetwork             RSSI: -46
	HomeNetwork             RSSI: -76
	Fios-12345              RSSI: -92
	FiOS-AB123              RSSI: -92
	NETGEAR53               RSSI: -93
Connecting to AP...
Connected to HomeNetwork 	RSSI: -45
My IP address is 192.168.1.245
IP lookup adafruit.com: 104.20.39.240
Ping google.com: 30 ms
Fetching text from http://wifitest.adafruit.com/testwifi/index.html
----------------------------------------
This is a test of Adafruit WiFi!
If you can read this, its working :)
----------------------------------------

Fetching json from http://wifitest.adafruit.com/testwifi/sample.json
----------------------------------------
{'fun': True, 'company': 'Adafruit', 'founded': 2005, 'primes': [2, 3, 5], 'pi': 3.14, 'mixed': [False, None, 3, True, 2.7, 'cheese']}
----------------------------------------
Done!
```

Going over the example above, here's a breakdown of what the program is doing:

- Initialize the ESP32 over SPI using the SPI port and 3 control pins:

```python
esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)

#...

else:
    spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
```

- Get the socket pool and the SSL context, and then tell the `adafruit_requests` library about them.

```python
pool = adafruit_connection_manager.get_radio_socketpool(esp)
ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp)
requests = adafruit_requests.Session(pool, ssl_context)
```

- Verify an ESP32 is found, checks the firmware and MAC address

```auto
if esp.status == adafruit_esp32spi.WL_IDLE_STATUS:
    print("ESP32 found and in idle mode")
print("Firmware vers.", esp.firmware_version)
print("MAC addr:", ":".join("%02X" % byte for byte in esp.MAC_address))
```

- Perform a scan of all access points it can see and print out the name and signal strength.

```python
for ap in esp.scan_networks():
    print("\t%-23s RSSI: %d" % (ap.ssid, ap.rssi))
```

- Connect to the AP we've defined here, then print out the local IP address. Then attempt to do a domain name lookup and ping google.com to check network connectivity. (Note sometimes the ping fails or takes a while; this isn't a big deal.)

```python
print("Connecting to AP...")
while not esp.is_connected:
    try:
        esp.connect_AP(ssid, password)
    except OSError as e:
        print("could not connect to AP, retrying: ", e)
        continue
print("Connected to", esp.ap_info.ssid, "\tRSSI:", esp.ap_info.rssi)
print("My IP address is", esp.ipv4_address)
print(
    "IP lookup adafruit.com: %s" % esp.pretty_ip(esp.get_host_by_name("adafruit.com"))
)
```

Now we're getting to the really interesting part of the example program. We've written a library for web fetching web data, named [adafruit\_requests](https://github.com/adafruit/Adafruit_CircuitPython_Requests). It is a lot like the regular Python library named [requests](https://requests.readthedocs.io/en/latest/). This library allows you to send HTTP and HTTPS requests easily and provides helpful methods for parsing the response from the server.

- Here is the part of the example program is fetching text data from a URL.

```python
TEXT_URL = "http://wifitest.adafruit.com/testwifi/index.html"  # Further up in the program

# ...

print("Fetching text from", TEXT_URL)
r = requests.get(TEXT_URL)
print('-' * 40)
print(r.text)
print('-' * 40)
r.close()
```

- Finally, here the program is fetching some JSON data. The `adafruit_requests` library will parse the JSON into a Python dictionary whose structure is the same as the structure of the JSON.

```auto
JSON_URL = "http://wifitest.adafruit.com/testwifi/sample.json"   # Further up in the program

# ...

print("Fetching json from", JSON_URL)
r = requests.get(JSON_URL)
print('-' * 40)
print(r.json())
print('-' * 40)
r.close()
```

# Advanced Requests Usage

Want to send custom HTTP headers, parse the response as raw bytes, or handle a response's http status code in your CircuitPython code?

We've written an&nbsp;example to show advanced usage of the requests module below.

To use with CircuitPython, you need to first install a few libraries, into the lib folder on your **CIRCUITPY** drive. Then you need to update **code.py** with the example script.

Thankfully, we can do this in one go. In the example below, click the **Download Project Bundle** button below to download the necessary libraries and the **code.py** file in a zip file. Extract the contents of the zip file, open the directory **examples/** and then click on the directory that matches the version of CircuitPython you're using and copy the contents of that directory to your **CIRCUITPY** drive.

https://github.com/adafruit/Adafruit_CircuitPython_Requests/blob/main/examples/esp32spi/requests_esp32spi_advanced.py

Your **CIRCUITPY** drive should now look similar to the following image:

![CIRCUITPY](https://adafruit.github.io/Adafruit_CircuitPython_Bundle/requests_esp32spi_requests_esp32spi_advanced.py.png )

# WiFi Manager

The way the examples above connect to WiFi works but it's a little finicky. Since WiFi is not necessarily so reliable, you may have disconnects and need to reconnect. For more advanced uses, we recommend using the `WiFiManager` class. It will wrap the connection/status/requests loop for you - reconnecting if WiFi drops, resetting the ESP32 if it gets into a bad state, etc.

Here's a more advanced example that shows using the `WiFiManager` and also how to fetch the current time from a web source.

https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI/blob/main/examples/esp32spi_localtime.py

# Further Information

For more information on the basics of doing networking in CircuitPython, see this guide:

### Networking in CircuitPython

[Networking in CircuitPython](https://learn.adafruit.com/networking-in-circuitpython)
# Discord and Slack Connected Smart Plant with Adafruit IO Actions

## Discord Setup

In this section, I'll guide you through the process of setting up your Discord server to handle the webhooks being sent from Adafruit IO.

First, click the **Add a Server** &nbsp;button on the left under the servers you're in.

![adafruit_io_Screenshot_from_2021-05-24_14-05-17.png](https://cdn-learn.adafruit.com/assets/assets/000/102/305/medium640/adafruit_io_Screenshot_from_2021-05-24_14-05-17.png?1621880949)

Then, click the **Create my Own** button. Or, if you'd like to use a different template, press one of the other buttons.

![adafruit_io_Screenshot_from_2021-05-24_14-05-46.png](https://cdn-learn.adafruit.com/assets/assets/000/102/306/medium640/adafruit_io_Screenshot_from_2021-05-24_14-05-46.png?1621880965)

Now, click **For me and my Friends**. You can choose a different option, but I won't be going over how to set them up (although the webhook setup should be exactly the same regardless of what type of server you have).

![adafruit_io_Screenshot_from_2021-05-24_14-06-07.png](https://cdn-learn.adafruit.com/assets/assets/000/102/307/medium640/adafruit_io_Screenshot_from_2021-05-24_14-06-07.png?1621881045)

Finally, name your server and press **Create.**

![adafruit_io_Screenshot_from_2021-05-24_14-06-47.png](https://cdn-learn.adafruit.com/assets/assets/000/102/308/medium640/adafruit_io_Screenshot_from_2021-05-24_14-06-47.png?1621881239)

Next, click on the icon for the server you just created and it should look something like this.

![adafruit_io_Screenshot_from_2021-05-24_14-34-45.png](https://cdn-learn.adafruit.com/assets/assets/000/102/309/medium640/adafruit_io_Screenshot_from_2021-05-24_14-34-45.png?1621881423)

Now that you're in the server, click on the name in the upper left-hand corner and press the **Server Settings** button.

![adafruit_io_Screenshot_from_2021-05-24_14-37-34.png](https://cdn-learn.adafruit.com/assets/assets/000/102/310/medium640/adafruit_io_Screenshot_from_2021-05-24_14-37-34.png?1621881525)

Click on the&nbsp; **Integrations&nbsp;** tab, then press&nbsp; **Create Webhook**.

![adafruit_io_Screenshot_from_2021-05-24_14-39-55.png](https://cdn-learn.adafruit.com/assets/assets/000/102/312/medium640/adafruit_io_Screenshot_from_2021-05-24_14-39-55.png?1621881631)

Rename your webhook and add a profile picture if you'd like to, then set the channel you'd like it to post in and click&nbsp; **Copy Webhook URL** and paste it somewhere. Then press&nbsp; **Save Changes**.

![adafruit_io_Screenshot_from_2021-05-24_14-49-57.png](https://cdn-learn.adafruit.com/assets/assets/000/102/313/medium640/adafruit_io_Screenshot_from_2021-05-24_14-49-57.png?1621882319)

# Discord and Slack Connected Smart Plant with Adafruit IO Actions

## Adafruit IO Setup

The first step is to make a feed that the device will write to.

Navigate to the **Feeds** tab and click&nbsp; **New Feed**. In this screenshot, I'm creating it in a group I made for this guide, but it's probably easier to just create it in the&nbsp; **Default** group.

![](https://cdn-learn.adafruit.com/assets/assets/000/137/044/medium640/adafruit_io_sz61wIQqPJ.png?1747924928)

Now, make your feed. I called mine&nbsp; **plant**.

![adafruit_io_Screenshot_from_2021-05-26_14-02-57.png](https://cdn-learn.adafruit.com/assets/assets/000/102/415/medium640/adafruit_io_Screenshot_from_2021-05-26_14-02-57.png?1622146148)

After you've created it, you should see it on your **Feeds** page.

![](https://cdn-learn.adafruit.com/assets/assets/000/137/045/medium640/adafruit_io_BKBVmDRZXk.png?1747925069)

## Create a New Action
Now that you've made the feed, go to the **Actions** tab and press&nbsp; **New Action**.

Then, enter a **Name** and **Description,** which you will see in the Actions list, helping you to understand the actions purpose in the future.

![](https://cdn-learn.adafruit.com/assets/assets/000/137/046/medium640/adafruit_io_aWKl74yzV9.png?1747925412)

To demonstrate that I've returned to the Actions list page, where you can see that I called the Action&nbsp;`Discord Plant Action` and included the description&nbsp;`Sends a message to Discord when the plant is thirsty`.

![](https://cdn-learn.adafruit.com/assets/assets/000/137/048/medium640/adafruit_io_Screenshot_2025-05-22_155635.png?1747925869)

You will have been forwarded to the newly created Action page, which you can revisit in future by clicking on the Action name from the Actions List, or use the checkbox to select the action in the list then use the&nbsp; **Edit** button.

All Actions need a trigger block, located under the&nbsp; **Triggers** category of the toolbox / sidebar. Open the Triggers category of the toolbox, then click and drag the one that includes the phrase&nbsp;`When FEED_X gets data that STARTS matching = 0` from the sidebar onto the main diagram, and place it into the Triggers: section of the diagram.

![](https://cdn-learn.adafruit.com/assets/assets/000/137/049/medium800thumb/adafruit_io_chrome_tveejo00sg.jpg?1747927091)

Then, select your feed in the dropdown of the trigger block just placed on the diagram. If you only have one feed then it will be selected already.

Click the dropdown for `Starts` just to have a quick look at the alternative options, and you can hover a mouse over the block to see the tooltip help message.

Leaving the Starts option as-is, move on to the Operator dropdown which defaults to Equals (`=`), and change that to be&nbsp; **Greater than or equal to&nbsp;** (`≥`).

Finally, in the text box at the end of the block, write the moisture value you want it to notify you at. I used 500, but that was really just a guess. My advice would be to wait until you normally would water your plants and then get the moisture value at that time.

![](https://cdn-learn.adafruit.com/assets/assets/000/137/057/medium640thumb/adafruit_io_chrome_Z427GK4UpT.jpg?1747938408)

Warning: 

Now for the output of the Action, setting up the Discord webhook. It's as simple as dropping the Webhook block onto the diagram followed by a small bit of configuration.

From the&nbsp; **Notifications&nbsp;** category in the toolbox, select the **Webhook** block and drag it into the `Actions:` section of the diagram.

Paste the discord webhook URL, copied in an earlier step, into the **URL:** text box.

Leave the checkbox for **Form Encode** unchecked to send as JSON data.

Copy and paste this template body into the paragraph block (nested in a template block which is the **POST Body:** of the webhook):  
`{"username": "plant-bot", "content": "Water your plant!"}`

If you want it to ping you, put your discord ID in the message with this formatting `<@discord_id>`.

![](https://cdn-learn.adafruit.com/assets/assets/000/137/062/medium640thumb/adafruit_io_chrome_GPqacXjGUO.jpg?1747941549)

[How to find your Discord ID](https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID-)
Your Action should now look like the image below. Assuming it does, save your Action using the **Save** button.

![](https://cdn-learn.adafruit.com/assets/assets/000/137/229/medium800/adafruit_io_image.png?1748459235)

# Discord and Slack Connected Smart Plant with Adafruit IO Actions

## Slack Setup

In this section, you'll go through the process of adding a webhook to a Slack workspace that you're already a part of.

First click on the name of the workspace, then&nbsp; **Settings & administration** (or just **Administration** is you're not an admin) and then **Manage apps**. If you're not an administrator, you should see less options on the last menu but the one you need should still be there.

![adafruit_io_Screenshot_from_2021-05-24_15-53-01.png](https://cdn-learn.adafruit.com/assets/assets/000/102/314/medium640/adafruit_io_Screenshot_from_2021-05-24_15-53-01.png?1621886608)

![adafruit_io_Screenshot_from_2021-05-26_13-43-15.png](https://cdn-learn.adafruit.com/assets/assets/000/102/358/medium640/adafruit_io_Screenshot_from_2021-05-26_13-43-15.png?1622051214)

Now click on&nbsp; **Custom integrations**.

![adafruit_io_Screenshot_from_2021-05-24_15-53-19.png](https://cdn-learn.adafruit.com/assets/assets/000/102/315/medium640/adafruit_io_Screenshot_from_2021-05-24_15-53-19.png?1621886737)

After that, type **Incoming WebHooks** into the search bar and click on the first result.

![adafruit_io_Screenshot_from_2021-05-24_15-54-06.png](https://cdn-learn.adafruit.com/assets/assets/000/102/316/medium640/adafruit_io_Screenshot_from_2021-05-24_15-54-06.png?1621886852)

Then, click&nbsp; **Add to Slack**.

![adafruit_io_Screenshot_from_2021-05-24_15-54-18.png](https://cdn-learn.adafruit.com/assets/assets/000/102/317/medium640/adafruit_io_Screenshot_from_2021-05-24_15-54-18.png?1621886902)

You should now see a screen that looks something like this. Here you'll decide which channel you want the webhook to post into. For this example, I'm using a direct message to myself, so I'll select my name from the list.

![adafruit_io_Screenshot_from_2021-05-24_15-54-47.png](https://cdn-learn.adafruit.com/assets/assets/000/102/318/medium640/adafruit_io_Screenshot_from_2021-05-24_15-54-47.png?1621886929)

Finally, you should see this screen. Copy the&nbsp; **Webhook URL** (the one I've put a red rectangle around) and paste it somewhere so you have it for the next section.

![adafruit_io_Screenshot_from_2021-05-24_15-55-13.png](https://cdn-learn.adafruit.com/assets/assets/000/102/319/medium640/adafruit_io_Screenshot_from_2021-05-24_15-55-13.png?1621886997)

# Discord and Slack Connected Smart Plant with Adafruit IO Actions

## Adafruit IO Setup

The first step is to make a feed that the device will write to.

Navigate to the **Feeds** tab and click&nbsp; **New Feed**. In this screenshot, I'm creating it in a group I made for this guide, but it's probably easier to just create it in the&nbsp; **Default** group.

![](https://cdn-learn.adafruit.com/assets/assets/000/137/065/medium640/adafruit_io_sz61wIQqPJ.png?1747953895)

Now, make your feed. I called mine&nbsp; **plant**.

![adafruit_io_Screenshot_from_2021-05-26_14-02-57.png](https://cdn-learn.adafruit.com/assets/assets/000/102/422/medium640/adafruit_io_Screenshot_from_2021-05-26_14-02-57.png?1622146417)

After you've created it, you should see it on your **Feeds** page.

![](https://cdn-learn.adafruit.com/assets/assets/000/137/066/medium640/adafruit_io_BKBVmDRZXk.png?1747954034)

## Create a New Action
Now that you've made the feed, go to the&nbsp; **Actions** &nbsp;tab and press&nbsp; **New Action**.

Then, enter a&nbsp; **Name&nbsp;** and&nbsp; **Description,&nbsp;** which you will see in the Actions list, helping you to understand the action's purpose in the future. In addition the Actions list has a search box, allowing you to easily find them once you have dozens of Actions in the list.

![](https://cdn-learn.adafruit.com/assets/assets/000/137/067/medium640/adafruit_io_aWKl74yzV9.png?1747954127)

To demonstrate why descriptions are useful, I've returned to the Actions list page, where you can see that I called the Action `Discord Plant Action`&nbsp;and included the description&nbsp;`Sends a message to Discord when the plant is thirsty`.

The 2nd image shows some rather unhelpfully named actions in my non-demo account, with no descriptions, which is really painful to return to in the future!

Obviously this Action should be for Slack not Discord! You can change an Action's name and description by using the Settings button while editing an Action.

![](https://cdn-learn.adafruit.com/assets/assets/000/137/068/medium640/adafruit_io_Screenshot_2025-05-22_155635.png?1747954243)

![](https://cdn-learn.adafruit.com/assets/assets/000/137/077/medium640/adafruit_io_Screenshot_2025-05-23_025421.png?1747965672)

![](https://cdn-learn.adafruit.com/assets/assets/000/137/075/medium640/adafruit_io_Screenshot_2025-05-23_024643.png?1747965317)

You will have been forwarded to the newly created Action page, which you can revisit in future by clicking on the Action name from the Actions List, or use the checkbox to select the action in the list then use the&nbsp; **Edit** &nbsp;button.

All Actions need a trigger block, located under the&nbsp; **Triggers** &nbsp;category of the toolbox / sidebar. Open the Triggers category of the toolbox, then click and drag the one that includes the phrase&nbsp;`When FEED_X gets data that STARTS matching = 0`&nbsp;from the sidebar onto the main diagram, and place it into the Triggers: section of the diagram.

![](https://cdn-learn.adafruit.com/assets/assets/000/137/069/medium800thumb/adafruit_io_chrome_tveejo00sg.jpg?1747954371)

Then, select your feed in the dropdown of the trigger block just placed on the diagram. If you only have one feed then it will be selected already.

Click the dropdown for&nbsp;`Starts`&nbsp;just to have a quick look at the alternative options, and you can hover a mouse over the block to see the tooltip help message.

Leaving the Starts option as-is, move on to the Operator dropdown which defaults to Equals (`=`), and change that to be&nbsp; **Greater than or equal to&nbsp;** (`≥`).

Finally, in the text box at the end of the block, write the moisture value you want it to notify you at. I used 500, but that was really just a guess. My advice would be to wait until you normally would water your plants and then get the moisture value at that time.

![](https://cdn-learn.adafruit.com/assets/assets/000/137/070/medium640thumb/adafruit_io_chrome_Z427GK4UpT.jpg?1747954915)

Warning: 

Now for the output of the Action, setting up the Slack webhook. It's as simple as dropping the Webhook block onto the diagram followed by a small bit of configuration.

From the&nbsp; **Notifications&nbsp;** category in the toolbox, select the&nbsp; **Webhook** &nbsp;block and drag it into the&nbsp;`Actions:`&nbsp;section of the diagram.

Paste the slack webhook URL, copied in an earlier step, into the&nbsp; **URL:** &nbsp;text box.

Leave the checkbox for&nbsp; **Form Encode** &nbsp;unchecked to send as JSON data.

Copy and paste this template body into the paragraph block (nested in a template block which is the&nbsp; **POST Body:** &nbsp;of the webhook):  
`{"text": "Water your plant!"}`

If you want it to do more fancy message things in Slack, then take a look at the Slack documentation.

![](https://cdn-learn.adafruit.com/assets/assets/000/137/079/medium640thumb/adafruit_io_chrome_YUGXstAUOS.jpg?1747966958)

[Making it Fancy! Slack webhook documentation](https://docs.slack.dev/messaging/sending-messages-using-incoming-webhooks#advanced_message_formatting)
Your Action should now look like the image below. Assuming it does, save your Action using the **Save** button.

![](https://cdn-learn.adafruit.com/assets/assets/000/137/228/medium800/adafruit_io_image.png?1748458494)

# Discord and Slack Connected Smart Plant with Adafruit IO Actions

## Code the Smart Plant

## Installing the Project Code

Download a zip of the project by clicking **Download Project Bundle** &nbsp;below.

After unzipping the file, copy&nbsp; **code.py** &nbsp;to the&nbsp; **CIRCUITPY** drive which appears when the Feather is connected to your computer via a USB cable.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/Webhook_Plant_Sensor/code.py

There are also a few libraries you'll need to copy over to the Feather:

- **adafruit\_esp32spi/**
- **adafruit\_minimqtt/**
- **adafruit\_io/**
- **adafruit\_seesaw/**
- **adafruit\_requests.mpy**
- **adafruit\_ticks.mpy**
- **adafruit\_pixelbuf.mpy**
- **adafruit\_connection\_manager.mpy**
- **neopixel.mpy**

Your **settings.toml** file should at the very least have values for the fields `CIRCUITPY_WIFI_SSID`, `CIRCUITPY_WIFI_PASSWORD`, `timezone`, `ADAFRUIT_AIO_USERNAME`, and `ADAFRUIT_AIO_KEY`. Make sure to copy it over to **CIRCUITPY** as well.

After you've done all that, this is what your&nbsp; **CIRCUITPY** drive should look like.

![CIRCUITPY drive showing required project files and libraries](https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/folder-images/Webhook_Plant_Sensor.png?raw=true )

## Wiring

Luckily there isn't really any wiring for this project. Put the Feather RP2040 and AirLift FeatherWing on the FeatherWing Doubler, then plug the STEMMA to STEMMA QT cable into both the Feather RP2040 and the moisture sensor.

![](https://cdn-learn.adafruit.com/assets/assets/000/102/456/medium800/adafruit_io_plant.png?1622228416)

## Assembly

Now that you've put the code on the Feather, plug the STEMMA cable in and put the sensor in your plant. I've found it works better if you put one side of the sensor against the edge of the pot.

![](https://cdn-learn.adafruit.com/assets/assets/000/102/395/medium800/adafruit_io_IMG_2046.jpg?1622059969)

## Code Usage

This project is rather easy to use. After you've set it up, put the sensor into the pot against the side, and plugged it in, it will send you a message if the moisture level is under 500, or whatever value you selected. If it is under that value, it won't send any more messages to Adafruit IO so that you don't get pinged every second until you water your plant.

Then, if the moisture value is above your value, it will send a message to Adafruit IO with the moisture value which shouldn't make the action send a webhook, and if the number dips below 500 again it will send another message that will make the action go off.

![](https://cdn-learn.adafruit.com/assets/assets/000/102/460/medium800thumb/adafruit_io_ezgif.com-gif-maker_%281%29.jpg?1622232234)

# Discord and Slack Connected Smart Plant with Adafruit IO Actions

## Code Run-Through

First, the code imports the required libraries.

```python
from os import getenv
import time

import board
from digitalio import DigitalInOut
from adafruit_esp32spi import adafruit_esp32spi
from adafruit_esp32spi import adafruit_esp32spi_wifimanager
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
import neopixel
import adafruit_minimqtt.adafruit_minimqtt as MQTT
from adafruit_io.adafruit_io import IO_MQTT
from adafruit_seesaw.seesaw import Seesaw
import busio
```

Next, the values we'll need in the main loop are created. The first variable, `low`, makes sure that you don't get a message every second when your plant needs to be watered, and `min` should match the value you are comparing the feed to in the Adafruit IO Action.

```auto
LOW = False
MIN = 500
```

Then, the moisture sensor gets initialized.

```python
i2c_bus = board.I2C()
seesaw = Seesaw(i2c_bus, addr=0x36)
```

After that, the code checks to see if **settings.toml** exists and then sets up the AirLift FeatherWing.

```python
ssid = getenv("CIRCUITPY_WIFI_SSID")
password = getenv("CIRCUITPY_WIFI_PASSWORD")
aio_username = getenv("ADAFRUIT_AIO_USERNAME")
aio_key = getenv("ADAFRUIT_AIO_KEY")

if None in [ssid, password, aio_username, aio_key]:
    raise RuntimeError(
        "WiFi and Adafruit IO settings are kept in settings.toml, "
        "please add them there. The settings file must contain "
        "'CIRCUITPY_WIFI_SSID', 'CIRCUITPY_WIFI_PASSWORD', "
        "'ADAFRUIT_AIO_USERNAME' and 'ADAFRUIT_AIO_KEY' at a minimum."
    )

# ...

# Set up WiFi
esp32_cs = DigitalInOut(board.D13)
esp32_ready = DigitalInOut(board.D11)
esp32_reset = DigitalInOut(board.D12)

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
status_pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
wifi = adafruit_esp32spi_wifimanager.WiFiManager(esp, ssid, password, status_pixel=status_pixel)
```

The callback functions are then defined which will be run when the device establishes a connection with Adafruit IO, when a new feed is subscribed to, and when a new message is received, respectively.

```python
# Define callback functions which will be called when certain events happen.
# pylint: disable=unused-argument
def connected(client):
    # This method is called when the client connects to Adafruit IO
    client.subscribe("plant")

def subscribe(client, userdata, topic, granted_qos):
    # This method is called when the client subscribes to a new feed.
    print("Subscribed to {0} with QOS level {1}".format(topic, granted_qos))

def message(client, feed_id, payload):
    # This method is called when a feed receives a new message
    print("Feed {0} received new value: {1}".format(feed_id, payload))
```

After the callback functions, the code connects to the internet and then to Adafruit IO.

```auto
# Connect to WiFi
print("Connecting to WiFi...")
wifi.connect()
print("Connected!")

# Initialize MQTT interface with the esp interface
MQTT.set_socket(socket, esp)

# Initialize a new MQTT Client object
mqtt_client = MQTT.MQTT(
    broker="io.adafruit.com",
	username=aio_username,
    password=aio_key,
)

# Initialize an Adafruit IO MQTT Client
io = IO_MQTT(mqtt_client)

# Connect the callback methods defined above to Adafruit IO
io.on_connect = connected
io.on_subscribe = subscribe
io.on_message = message

# Connect to Adafruit IO
print("Connecting to Adafruit IO...")
io.connect()
```

Before the main loop, the code defines a variable, `start`. This variable is used to make sure that the feed isn't published to more often than desired.

```python
START = 0
```

The first thing that happens in the main loop is the sensor's temperature and moisture are read. The temperature isn't used in this code, but I left it in there just in case someone wants to add temperature to their project.

Then the code checks if the value from the sensor is below the specified threshold. It then checks to see if this is the first time since it was last above the threshold that it has published a value. This is done so that when the sensor is below the threshold, the webhook isn't activated all the time. It then publishes the value.

In the next if block, the code checks to see if the sensor value is above the threshold and then checks to see if it's been 10 seconds since the last value was published and if it has been ten seconds it publishes the value.

Finally, the code prints the sensor values and the loop starts over again.

```auto
while True:
    # read moisture level through capacitive touch pad
    touch = seesaw.moisture_read()

    # read temperature from the temperature sensor
    temp = seesaw.get_temp()

    if touch &lt; MIN:
        if not LOW:
            io.publish("plant", touch)
            print("published")
        LOW = True

    elif touch &gt;= MIN and time.time() - START &gt; 10:
        io.publish("plant", touch)
        print("published to Adafruit IO")
        START = time.time()
        LOW = False

    print("temp: " + str(temp) + "  moisture: " + str(touch))
    time.sleep(1)
```


## Featured Products

### Adafruit Feather RP2040

[Adafruit Feather RP2040](https://www.adafruit.com/product/4884)
A new chip means a new Feather, and the Raspberry Pi RP2040 is no exception. When we saw this chip we thought "this chip is going to be awesome when we give it the Feather Treatment" and so we did! This Feather features the&nbsp; **RP2040** , and all niceties you know and...

In Stock
[Buy Now](https://www.adafruit.com/product/4884)
[Related Guides to the Product](https://learn.adafruit.com/products/4884/guides)
### Adafruit STEMMA Soil Sensor - I2C Capacitive Moisture Sensor

[Adafruit STEMMA Soil Sensor - I2C Capacitive Moisture Sensor](https://www.adafruit.com/product/4026)
Most low cost soil sensors are _resistive_ style, where there's two prongs and the sensor measures the conductivity between the two. These work OK at first, but eventually start to oxidize because of the exposed metal. Even if they're gold plated! The resistivity measurement...

Out of Stock
[Buy Now](https://www.adafruit.com/product/4026)
[Related Guides to the Product](https://learn.adafruit.com/products/4026/guides)
### 4-pin JST PH to JST SH Cable - STEMMA to QT / Qwiic

[4-pin JST PH to JST SH Cable - STEMMA to QT / Qwiic](https://www.adafruit.com/product/4424)
Are you a maker in the midst of&nbsp;a [**STEMMA**](https://learn.adafruit.com/introducing-adafruit-stemma-qt/what-is-stemma) dilemma? This 200mm long 4-wire cable is a fantastic chimera-cable fitted with **STEMMA QT / Sparkfun Qwiic JST SH** on one end,...

In Stock
[Buy Now](https://www.adafruit.com/product/4424)
[Related Guides to the Product](https://learn.adafruit.com/products/4424/guides)
### Adafruit AirLift FeatherWing – ESP32 WiFi Co-Processor

[Adafruit AirLift FeatherWing – ESP32 WiFi Co-Processor](https://www.adafruit.com/product/4264)
Give your Feather project a _lift_ with the Adafruit AirLift FeatherWing - a FeatherWing that lets you use the powerful ESP32 as a WiFi co-processor. You probably have your favorite Feather ([like the Feather M4](https://www.adafruit.com/product/3857)) that comes with its own...

In Stock
[Buy Now](https://www.adafruit.com/product/4264)
[Related Guides to the Product](https://learn.adafruit.com/products/4264/guides)
### FeatherWing Doubler - Prototyping Add-on For All Feather Boards

[FeatherWing Doubler - Prototyping Add-on For All Feather Boards](https://www.adafruit.com/product/2890)
This is the **FeatherWing Doubler** - a prototyping add-on and more for all Feather boards. This is similar to our [FeatherWing Proto](https://www.adafruit.com/products/2884) except there are two! The magic of the Doubler comes when stacking a Feather and another...

In Stock
[Buy Now](https://www.adafruit.com/product/2890)
[Related Guides to the Product](https://learn.adafruit.com/products/2890/guides)
### USB Type A to Type C Cable - approx 1 meter / 3 ft long

[USB Type A to Type C Cable - approx 1 meter / 3 ft long](https://www.adafruit.com/product/4474)
As technology changes and adapts, so does Adafruit. This&nbsp;&nbsp; **USB Type A to Type C** cable will help you with the transition to USB C, even if you're still totin' around a USB Type A hub, computer or laptop.

USB C is the latest industry-standard connector for...

In Stock
[Buy Now](https://www.adafruit.com/product/4474)
[Related Guides to the Product](https://learn.adafruit.com/products/4474/guides)

## Related Guides

- [Adafruit STEMMA Soil Sensor - I2C Capacitive Moisture Sensor](https://learn.adafruit.com/adafruit-stemma-soil-sensor-i2c-capacitive-moisture-sensor.md)
- [Adafruit AirLift FeatherWing - ESP32 WiFi Co-Processor](https://learn.adafruit.com/adafruit-airlift-featherwing-esp32-wifi-co-processor-featherwing.md)
- [Introducing Adafruit Feather RP2040](https://learn.adafruit.com/adafruit-feather-rp2040-pico.md)
- [Adafruit STEMMA & STEMMA QT](https://learn.adafruit.com/introducing-adafruit-stemma-qt.md)
- [Hacking Holiday Animatronics](https://learn.adafruit.com/hacking-holiday-animatronics.md)
- [Easy Alexa (Echo) Control of your ESP8266 Huzzah](https://learn.adafruit.com/easy-alexa-or-echo-control-of-your-esp8266-huzzah.md)
- [No-Code IoT Soil Sensor](https://learn.adafruit.com/soil-node.md)
- [3D Printed Frame for Adafruit IS31FL3741 LED Glasses](https://learn.adafruit.com/3d-printed-frame-for-led-glasses-is31fl3741.md)
- [MIDI Laser Harp with Time of Flight Distance Sensors](https://learn.adafruit.com/midi-laser-harp-time-of-flight-sensors.md)
- [Walkmp3rson: Personal MP3 'Tape' Player](https://learn.adafruit.com/walkmp3rson-personal-mp3-tape-player.md)
- [32x32 Square Pixel Art Animation Display](https://learn.adafruit.com/32x32-square-pixel-display.md)
- [Bluefruit LE Feather Robot Rover](https://learn.adafruit.com/bluefruit-feather-robot.md)
- [Digital Clock with CircuitPython](https://learn.adafruit.com/digital-clock-with-circuitpython.md)
- [Soundboard Speaker for Bikes & Scooters](https://learn.adafruit.com/soundboard-speaker-for-bikes-scooters.md)
- [MIDI Controlled Robot Lyre with CircuitPython](https://learn.adafruit.com/midi-controlled-robot-lyre-with-circuitpython.md)
- [Quickstart - Raspberry Pi RP2040 with BLE and CircuitPython](https://learn.adafruit.com/quickstart-raspberry-pi-rp2040-with-ble-and-circuitpython.md)
- [Robotic AI Bear using ChatGPT](https://learn.adafruit.com/robotic-ai-bear-using-chatgpt.md)
