Clawctl
Use Case
10 min

I Built a $12 Sensor That Texts Me When My Basement Floods (ESP32 + OpenClaw)

Forget cloud subscriptions. A $12 ESP32, a water sensor, and an AI agent saved me from a $15,000 flood. Full Arduino code included.

Clawctl Team

Product & Engineering

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

ComponentCostLink
ESP32 dev board$6Amazon, AliExpress
Water level sensor$3Amazon
USB power adapter$3You 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?

Deploy your IoT agent in 60 seconds →

This content is for informational purposes only and does not constitute financial, legal, medical, tax, or other professional advice. Individual results vary. See our Terms of Service for important disclaimers.

Ready to deploy your OpenClaw securely?

Get your OpenClaw running in production with Clawctl's enterprise-grade security.