I Built a $12 Sensor That Texts Me When My Basement Floods (ESP32 + OpenClaw)
Three months ago, my sump pump failed while I was at work.
By the time I got home, there was 4 inches of water in my basement. The damage estimate? $15,000.
I'm an engineer. I should have seen this coming. But commercial water sensors either cost $200+ or require monthly subscriptions. And the cheap ones on Amazon? No API. No webhooks. Just a loud beep that nobody hears.
So I built my own. Total cost: $12.
Now I get a text message within 30 seconds of detecting water. My AI agent even checks the weather forecast—if heavy rain is coming, it sends me a preemptive warning.
Here's exactly how to build it.
Security first: This guide uses webhooks accessible from the internet. Make sure your OpenClaw instance is properly secured—or deploy with Clawctl for automatic hardening.
What You'll Need
| Component | Cost | Link |
|---|---|---|
| ESP32 dev board | $6 | Amazon, AliExpress |
| Water level sensor | $3 | Amazon |
| USB power adapter | $3 | You probably have one |
| Total | $12 |
Optional: DHT22 temp/humidity sensor ($4) if you want environmental monitoring too.
The Architecture
Sensor → ESP32 → HTTP POST → OpenClaw Webhook → AI Agent → Twilio SMS
No cloud subscription. No vendor lock-in. Your data, your rules.
Step 1: Wire It Up
Connect the water sensor to your ESP32:
- VCC → 3.3V
- GND → GND
- Signal → GPIO 34 (any ADC pin works)
That's it. The sensor outputs an analog voltage that increases when water is detected.
Step 2: The Arduino Code
This is the complete, working code. Copy it, change your WiFi credentials and webhook URL, and flash it.
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
// Configuration
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* webhookUrl = "https://your-tenant.clawctl.com/webhook/sensors";
const char* apiKey = "YOUR_API_KEY";
// Pin definitions
const int WATER_SENSOR_PIN = 34;
const int LED_PIN = 2; // Built-in LED
// Thresholds
const int WATER_THRESHOLD = 500; // Adjust based on your sensor
const unsigned long SEND_INTERVAL = 60000; // Normal: every 60 seconds
const unsigned long ALERT_INTERVAL = 5000; // Alert: every 5 seconds
unsigned long lastSend = 0; bool alertActive = false;
void setup() { Serial.begin(115200); pinMode(WATER_SENSOR_PIN, INPUT); pinMode(LED_PIN, OUTPUT);
// Connect to WiFi WiFi.begin(ssid, password); Serial.print("Connecting to WiFi"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(" Connected!"); Serial.print("IP: "); Serial.println(WiFi.localIP()); }
void loop() { int waterLevel = analogRead(WATER_SENSOR_PIN); bool waterDetected = waterLevel > WATER_THRESHOLD;
// Visual indicator digitalWrite(LED_PIN, waterDetected ? HIGH : LOW);
// Determine send interval based on alert state unsigned long interval = waterDetected ? ALERT_INTERVAL : SEND_INTERVAL;
if (millis() - lastSend >= interval) { sendData(waterLevel, waterDetected); lastSend = millis(); }
delay(100); }
void sendData(int waterLevel, bool waterDetected) { if (WiFi.status() != WL_CONNECTED) { Serial.println("WiFi disconnected, reconnecting..."); WiFi.reconnect(); return; }
HTTPClient http; http.begin(webhookUrl); http.addHeader("Content-Type", "application/json"); http.addHeader("Authorization", String("Bearer ") + apiKey);
// Build JSON payload StaticJsonDocument<256> doc; doc["device_id"] = "basement-water-01"; doc["sensor_type"] = "water_level"; doc["value"] = waterLevel; doc["alert"] = waterDetected; doc["battery"] = 100; // USB powered doc["rssi"] = WiFi.RSSI();
String payload; serializeJson(doc, payload);
int httpCode = http.POST(payload);
if (httpCode > 0) { Serial.printf("POST success: %d\n", httpCode); } else { Serial.printf("POST failed: %s\n", http.errorToString(httpCode).c_str()); }
http.end(); }
**Libraries you'll need** (install via Arduino IDE Library Manager):
- ArduinoJson by Benoit Blanchon
- HTTPClient (included with ESP32 board package)
## Step 3: Set Up Your OpenClaw Webhook
Your OpenClaw agent needs to know what to do when data arrives.
```yaml
# basement-monitor.yaml
on_event: webhook.sensors
triggers:
- condition: "event.alert == true"
actions:
- name: send_sms
tool: twilio
params:
to: "+1234567890"
message: "ALERT: Water detected in basement! Level: {{ event.value }}. Check immediately."
- name: log_alert
tool: database
params:
table: alerts
data:
device: "{{ event.device_id }}"
level: "{{ event.value }}"
timestamp: "{{ now }}"
- condition: "event.alert == false"
actions:
- name: log_reading
tool: database
params:
table: sensor_readings
data:
device: "{{ event.device_id }}"
value: "{{ event.value }}"
rssi: "{{ event.rssi }}"
Step 4: The Smart Part (Weather Correlation)
Here's where it gets interesting. My agent doesn't just react to water—it predicts problems.
# weather-correlation.yaml
schedule: "0 6 * * *" # Every day at 6am
actions:
- name: check_forecast
tool: weather_api
params:
location: "Chicago, IL"
days: 2
- condition: "forecast.rain_probability > 70 AND forecast.rain_inches > 1"
actions:
- name: preemptive_warning
tool: twilio
params:
to: "+1234567890"
message: "Heavy rain expected ({{ forecast.rain_inches }}in). Check sump pump before it starts."
Now I get a heads-up before problems happen. The AI checks the weather, looks at my historical sensor data, and warns me when conditions are risky.
Why This Actually Works
I've been running this setup for 3 months. Results:
- 2 real alerts — both times, water was starting to accumulate. Fixed in minutes.
- 4 preemptive warnings — checked the sump pump before storms, found a clog once.
- $0 in water damage — vs $15,000 before.
- $0/month — no subscriptions, no vendor lock-in.
The ESP32 sends data every 60 seconds during normal operation. When it detects water, it switches to every 5 seconds and the alert flag triggers immediate SMS notification.
Scaling Up
Once you have this working, you can add more sensors:
- Temperature/humidity (DHT22)
- Motion detection (PIR)
- Door/window sensors (magnetic reed switches)
- Air quality (MQ series)
Same pattern. Same code structure. One AI agent correlating all of them.
{
"device_id": "garage-environment",
"sensors": {
"temperature": 22.5,
"humidity": 45,
"motion": false,
"door_open": true
}
}
Your agent sees everything. "Garage door has been open for 2 hours and temperature is dropping below freezing. Close it?"
Security Note
Your ESP32 is hitting a public endpoint. Clawctl handles:
- Gateway authentication — API key validation on every request
- Rate limiting — prevent runaway devices from flooding your agent
- Audit logs — every sensor reading recorded and searchable
- Egress control — your agent can only call approved services (Twilio, weather APIs, etc.)
Don't expose raw OpenClaw to the internet. Use Clawctl.
Ready to build your own sensor network?