Scenario: Design all interfaces (hardware + software) for a solar-powered smart greenhouse controller monitoring 3 zones with temp/humidity/soil sensors, controlling irrigation valves and ventilation fan, sending data to cloud dashboard every 5 minutes.
Hardware Layer - Physical Interfaces:
Sensors per zone (3 zones total): - BME280 (temp + humidity): I2C address 0x76 - Capacitive soil moisture: Analog output 0-3.3V
Actuators:
- 3x irrigation solenoid valves: GPIO with relay (12V, 150mA each)
- 1x ventilation fan: PWM GPIO (12V, 500mA)
I2C Bus Design (GPIO 21 SDA, GPIO 22 SCL):
ESP32 ----I2C----> BME280 zone A (0x76)
|-> BME280 zone B (0x77) <-- Changed address via SDO pin
|-> BME280 zone C (0x40) <-- Using alternate BME688
ADC Channels (ESP32 has 18 ADC pins): - GPIO 32: Soil sensor zone A - GPIO 33: Soil sensor zone B - GPIO 34: Soil sensor zone C - GPIO 35: Solar panel voltage monitor
GPIO Outputs:
- GPIO 25: Valve zone A
- GPIO 26: Valve zone B
- GPIO 27: Valve zone C
- GPIO 14: Fan PWM control
- GPIO 2: Status LED
Pin Budget: 2 (I2C) + 4 (ADC) + 5 (GPIO out) = 11 of 34 GPIO used (67% headroom for expansion)
Software Layer - MQTT Topics:
greenhouse/zone_a/temperature → 23.5 (°C)
greenhouse/zone_a/humidity → 65 (%)
greenhouse/zone_a/soil_moisture → 420 (0-1023 ADC)
greenhouse/zone_b/temperature → 24.1
[... similar for zones B, C ...]
greenhouse/control/valve_a/command ← "OPEN" or "CLOSE"
greenhouse/control/valve_b/command
greenhouse/control/valve_c/command
greenhouse/control/fan/command ← 0-255 (PWM duty cycle)
greenhouse/status/battery_voltage → 12.6 (V)
greenhouse/status/uptime → 86400 (seconds)
Cloud Layer - REST API:
GET /api/greenhouses/gh-001/zones/a/readings
POST /api/greenhouses/gh-001/zones/a/irrigation/start
GET /api/greenhouses/gh-001/status
Data Flow Example (Zone A irrigation decision):
1. BME280 → I2C read (0x76) → ESP32 firmware → temp = 28.5°C, humidity = 45%
2. Soil sensor → ADC read (GPIO 32) → raw = 680 → moisture = 66%
3. Firmware logic: IF (temp > 28°C AND humidity < 50% AND moisture < 70%) THEN irrigate
4. MQTT publish: greenhouse/zone_a/temperature = 28.5
greenhouse/zone_a/humidity = 45
greenhouse/zone_a/soil_moisture = 66
5. Cloud receives → Dashboard shows alert "Zone A needs water"
6. User clicks "Start Irrigation" OR automated rule triggers
7. Cloud publishes: greenhouse/control/valve_a/command = "OPEN"
8. ESP32 subscribes → receives command → GPIO 25 HIGH → relay closes → valve opens
9. After 10 minutes → GPIO 25 LOW → valve closes
10. MQTT publish: greenhouse/zone_a/irrigation_event = {start: timestamp, duration: 600s}
Interface Performance Analysis:
I2C Bus Timing (3 sensors, 400 kHz Fast Mode): - BME280 read: ~10 bytes at 400 kHz = 0.2 ms per sensor - All 3 sensors: 0.6 ms every 5 minutes = 0.0002% bus utilization (negligible)
MQTT Message Rate:
- 3 zones × 3 readings = 9 messages every 5 minutes = 2,592 messages/day
- Message size: ~50 bytes average
- Daily data: 2,592 × 50 = 129.6 KB/day (Wi-Fi easily handles this)
Power Budget:
- Sleep (99% of time): 10 µA
- Wake + I2C read (0.6 ms): 20 mA
- Wi-Fi TX (2 seconds): 180 mA
- Solar panel (10cm²): 100 mW/cm² × 10 × 0.2 efficiency = 200 mW = 50 mA at 4V (6 hours sun/day average)
- Daily harvest: 50 mA × 6 hours = 300 mAh
- Daily consumption: ~3 mAh (with 99% sleep)
- Result: Solar provides 100x required energy → system runs indefinitely
Why This Design Works:
- I2C reduces wiring: 3 sensors on 2 wires vs 9 wires for individual connections
- MQTT provides bidirectional control: Dashboard can send commands without polling
- Solar + battery buffering: 200 mW solar + 2000 mAh battery handles 3 consecutive cloudy days
- Headroom for expansion: 67% GPIO available, I2C bus can add 124 more devices, MQTT bandwidth used <1%
Interface Lessons:
- Start with hardware constraints (available GPIO, I2C addresses)
- Design MQTT topics before writing firmware (prevents refactoring)
- Calculate power budget early (drives sleep vs active time decisions)
- Leave 50%+ headroom for future features (customers always want “just one more sensor”)