Installing the Project Code
Download a zip of the project by clicking Download Project Bundle below.
After unzipping the file, copy code.py to the CIRCUITPY drive which appears when the Feather is connected to your computer via a USB cable.
# SPDX-FileCopyrightText: 2021 Eva Herrada for Adafruit Industries
# SPDX-License-Identifier: MIT
from os import getenv
import time
import board
from digitalio import DigitalInOut
import adafruit_connection_manager
from adafruit_esp32spi import adafruit_esp32spi
from adafruit_esp32spi import adafruit_esp32spi_wifimanager
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
# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml
# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.)
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."
)
# Used to make sure that the Adafruit IO Trigger is only run once when the moisture value is below
# the desired threshold, set in MIN
LOW = False
# The minimum moisture value. If the value is below this number, it will activate the Adafruit IO
# trigger. This number should match the number you set in your Adafruit IO trigger. Feel free
# to mess around and try out different moisture values as how wet this actually is can vary a lot
# depending on where the sensor is and the soil in the pot.
MIN = 500
# Set up moisture sensor with seesaw
i2c = board.I2C() # uses board.SCL and board.SDA
# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller
seesaw = Seesaw(i2c, addr=0x36)
# 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)
# 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))
# Connect to WiFi
print("Connecting to WiFi...")
wifi.connect()
print("Connected!")
pool = adafruit_connection_manager.get_radio_socketpool(esp)
ssl_context = adafruit_connection_manager.get_radio_ssl_context(esp)
# Initialize a new MQTT Client object
mqtt_client = MQTT.MQTT(
broker="io.adafruit.com",
username=aio_username,
password=aio_key,
socket_pool=pool,
ssl_context=ssl_context,
)
# 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()
plant_feed = "plant"
START = 0
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 < MIN:
if not LOW:
io.publish(plant_feed, touch)
print("published")
LOW = True
elif touch >= MIN and time.time() - START > 10:
io.publish(plant_feed, touch)
print("published to Adafruit IO")
START = time.time()
LOW = False
print("temp: " + str(temp) + " moisture: " + str(touch))
time.sleep(1)
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 CIRCUITPY drive should look like.
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.
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.
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.
Page last edited April 16, 2025
Text editor powered by tinymce.