Overview
Almost a year ago, I moved from the city to the countryside and started a new project: a small greenhouse. Manually managing the watering schedule quickly became a repetitive task, so I decided to merge my passion for technology with gardening.
This article describes how I built a smart, automated watering system. Using a Raspberry Pi Pico W, several sensors, and the power of the AWS cloud with its AI models, the system now monitors environmental conditions and autonomously decides the best time to water the plants.

π§ Hardware Architecture and Components
The heart of the system is a Raspberry Pi Pico W that orchestrates reading data from 20 soil moisture sensors and one environmental sensor. To handle such a large number of analog sensors with the Pico’s limited pins, I used two multiplexers.
Components Needed
- Raspberry Pi Pico W: The microcontroller with Wi-Fi that runs the MicroPython code.
- 20x Capacitive Soil Moisture Sensors V2.0: To measure soil moisture.
- 2x CD74HC4067 Analog Multiplexers: To expand the Pico’s analog inputs.
- 1x BME280 Environmental Sensor: To measure air temperature, humidity, and pressure.
- Breadboard, jumper wires, and a power supply.
Wiring Diagram
The wiring is the most complex part. The two multiplexers (Mux) are controlled by shared digital pins (S0-S3) but have separate enable (EN) pins to activate them one at a time. The outputs (Z) of each Mux are connected to two distinct ADC pins on the Pico.

Ok that’s a lot of wires, but here’s how it works practically speaking:
+---------------------+
| Raspberry Pico W |
| |
GP2 βββ| S0 β |
GP3 βββ| S1 β Shared |
GP10 βββ| S2 β between Muxes |
GP11 βββ| S3 β |
| |
GP6 βββ| EN (Mux #1) |
GP26 ββ| Z (ADC0) |ββ Mux #1 (Sensors 0-15)
| |
GP7 βββ| EN (Mux #2) |
GP27 ββ| Z (ADC1) |ββ Mux #2 (Sensors 16-19)
| |
GP4 βββ| SDA (IΒ²C0) |ββ BME280 SDA
GP5 βββ| SCL (IΒ²C0) |ββ BME280 SCL
| |
3V3 βββ| 3V3 & VCC |
GND βββ| GND |
+---------------------+
π₯οΈ MicroPython Code for Data Collection
The code on the Pico W handles selecting each sensor via the multiplexers, reading moisture values, collecting environmental data from the BME280, and sending everything to AWS via an HTTP POST request.
# main.py on the Raspberry Pi Pico W
import time
import json
import urequests
from machine import Pin, ADC, I2C
import bme280
# --- Wi-Fi Configuration ---
WIFI_SSID = "YOUR_WIFI_SSID"
WIFI_PASSWORD = "YOUR_WIFI_PASSWORD"
# --- AWS API Gateway Endpoint ---
API_ENDPOINT = "https://your-api-gateway-url.amazonaws.com/ingest"
# --- Pin Configuration ---
# Mux select pins (shared)
s_pins = [Pin(2, Pin.OUT), Pin(3, Pin.OUT), Pin(10, Pin.OUT), Pin(11, Pin.OUT)]
# Enable and ADC pins for each Mux
muxes = [
{'en': Pin(6, Pin.OUT), 'adc': ADC(Pin(26))},
{'en': Pin(7, Pin.OUT), 'adc': ADC(Pin(27))}
]
# I2C for BME280
i2c = I2C(0, scl=Pin(5), sda=Pin(4))
bme = bme280.BME280(i2c=i2c)
# --- Functions ---
def connect_wifi():
import network
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print('Connecting to WiFi...')
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
while not wlan.isconnected():
time.sleep(1)
print('Connected:', wlan.ifconfig())
def select_channel(channel):
for i in range(4):
s_pins[i].value((channel >> i) & 1)
def read_sensors():
soil_readings = []
# Read the 16 channels of the first Mux
muxes[0]['en'].low()
muxes[1]['en'].high()
for i in range(16):
select_channel(i)
time.sleep_ms(50)
soil_readings.append(muxes[0]['adc'].read_u16())
# Read the 4 channels of the second Mux
muxes[0]['en'].high()
muxes[1]['en'].low()
for i in range(4):
select_channel(i)
time.sleep_ms(50)
soil_readings.append(muxes[1]['adc'].read_u16())
muxes[1]['en'].high() # Disable both muxes
return soil_readings
# --- Main Loop ---
connect_wifi()
while True:
try:
soil_moisture_data = read_sensors()
temp, pressure, humidity = bme.read_compensated_data()
payload = {
"device_id": "pico_greenhouse_1",
"timestamp": time.time(),
"soil_moisture": soil_moisture_data,
"temperature": temp / 100,
"humidity": humidity / 1024
}
print("Sending data:", json.dumps(payload))
response = urequests.post(API_ENDPOINT, headers={'Content-Type': 'application/json'}, data=json.dumps(payload))
print("Response:", response.status_code)
response.close()
except Exception as e:
print("Error:", e)
# Try to reconnect in case of network issues
connect_wifi()
# Wait 15 minutes before the next reading
time.sleep(900)
βοΈ Cloud Architecture on AWS
The data sent from the Pico is processed by a serverless architecture on AWS, which handles storing it, analyzing it with AI, and sending notifications.

- API Gateway: Provides an HTTP endpoint to receive data from the Pico.
- Lambda (Ingest): A function triggered by API Gateway, which validates the data and saves it to a DynamoDB table.
- DynamoDB: A NoSQL database where all sensor readings are stored.
- EventBridge (Scheduler): A service that triggers a second Lambda function at regular intervals (e.g., every 6 hours).
- Lambda (Analysis): This function retrieves recent data from DynamoDB, builds a prompt for AWS Bedrock, and invokes an AI model (like Claude 3 Sonnet) to get a decision.
- AWS Bedrock: The AI service that analyzes the data and decides if watering is necessary.
- SNS (Simple Notification Service): If Bedrock responds affirmatively, the analysis Lambda publishes a message to an SNS topic.
- User Notification: SNS sends the message to the topic’s subscribers, for example, via email or SMS.
Analysis Logic with Bedrock
The analysis Lambda function sends a prompt similar to this to Bedrock:
You are an expert agronomist. Analyze the following data from a greenhouse. Soil moisture is measured from 0 (very dry) to 65535 (in water). Values below 30000 indicate dry soil.
Data:
- Average Temperature: 24.5Β°C
- Average Air Humidity: 65%
- Soil Moisture Readings (last 20): [28500, 31000, 29000, ..., 45000]
- Average Soil Moisture: 34500
Based on this data, do the plants need to be watered? Respond only with 'YES' or 'NO' and provide a concise explanation of no more than 30 words.
π£ Notification System
When the analysis Lambda receives a “YES” from Bedrock, it sends a notification via SNS. This allows me to receive an email or a text message on my phone, alerting me to activate the irrigation (or, in a future version, to activate it automatically).
Subject: πΏ Greenhouse Alert: Your Plants Need Watering
Body:
Hi there,
Our AI agronomist has analyzed the latest environmental and soil moisture data from your greenhouse.
β
Verdict: YES β It's time to water your plants.
Summary:
π‘οΈ Temperature: 24.5Β°C
π§ Humidity: 65%
π± Average Soil Moisture: 28,500 (Dry)
π
Timestamp: July 22, 2025 β 08:45 AM UTC
π‘ Explanation: Soil moisture levels are consistently below the optimal threshold. Watering is recommended to maintain healthy plant growth.
You can activate the irrigation system manually or let the next scheduled automation handle it (if enabled).
Happy gardening!
β Your Smart Greenhouse System π€πΏ
πΏ Conclusion and Future Developments
This project is a fascinating example of how IoT and AI can solve practical problems. It has transformed a manual task into an intelligent, autonomous system that takes optimal care of my plants.
Future Developments:
- Automatic Irrigation: Add a relay controlled by the Pico to activate a water pump based on the AWS response.
- Monitoring Dashboard: Create a web dashboard with Grafana or Amazon QuickSight to visualize historical data.
- Custom AI Model: Train a specific AI model for my plants and my greenhouse conditions.