Water Level Sensor : R/arduino

Aug 04, 2026

Leave a message

Arduino Water Level Sensor with MQTT and Home Assistant

Once you have a working Arduino water level sensor on your bench, the next question is always the same: how do I get the data onto my phone? A standalone sensor that prints values to the Serial Monitor is a prototype. A sensor that publishes level data to your phone, triggers automations, and logs history is a real system. The bridge between Arduino and the smart home is MQTT - a lightweight publish-subscribe messaging protocol that works over Wi-Fi, BLE, or LoRa and integrates natively with Home Assistant, openHAB, Node-RED, and any custom dashboard.

This guide covers the full MQTT-to-Home-Assistant pipeline for Arduino water level sensors: setting up the MQTT broker (Mosquitto), configuring Home Assistant, programming the Arduino ESP32 to publish level data, using Home Assistant's auto-discovery, building a dashboard, and adding alert automations. Everything runs on free open-source software on hardware that costs under $15.

For sensor fundamentals see what is a liquid level sensor; for continuous vs point-level see continuous vs point level sensors; for Arduino water level projects see water level sensor Arduino; for sensor types see water level sensor calibration and accuracy; for non-contact options see non-contact liquid level sensors; and the ultimate guide to liquid level switches.


How MQTT Works for Water Level Sensors

MQTT is built on three concepts: the broker, the publisher, and the subscriber.

The broker is a server (typically running on a Raspberry Pi or a cloud VPS) that receives messages published by sensors and forwards them to any subscriber that has expressed interest. The broker does not store data long-term - it is a routing switch.

The publisher is your Arduino. It connects to the broker over Wi-Fi and sends messages to specific topics. A topic is a hierarchical string that describes the data, for example:

code复制

home/basement/tank/level home/basement/tank/temperature home/basement/tank/status

The subscriber is Home Assistant. It connects to the same broker and subscribes to topics. When a message arrives on a topic Home Assistant is subscribed to, it parses the payload and updates the corresponding sensor entity.

MQTT uses a quality of service (QoS) level. QoS 0 means "fire and forget" - the broker delivers the message once, with no confirmation. QoS 1 means "at least once" - the broker retries until it gets an acknowledgement. QoS 2 means "exactly once" - guaranteed delivery but slower. For water level telemetry, QoS 0 is sufficient; for critical alarms, QoS 1 is better.

The payload format for Home Assistant MQTT integration is typically JSON or a plain number. A plain number payload is simpler and works for most level sensor use cases:

code复制

home/basement/tank/level → "67" home/basement/tank/status → "online"


Setting Up the MQTT Broker on Raspberry Pi

The most common MQTT broker is Mosquitto, which runs on a Raspberry Pi Zero 2 W (~$15) with no issues. Install it with:

bash复制

sudo apt update sudo apt install -y mosquitto mosquitto-clients sudo systemctl enable mosquitto

Create a password file for authentication:

bash复制

sudo mosquitto_passwd -c /etc/mosquitto/passwd your_username # Enter password when prompted

Configure Mosquitto to require authentication:

bash复制

sudo nano /etc/mosquitto/conf.d/default.conf

Add:

code复制

listener 1883 allow_anonymous false password_file /etc/mosquitto/passwd

Apply:

bash复制

sudo systemctl restart mosquitto

Test from any machine on the network:

bash复制

mosquitto_sub -h <pi-ip-address> -u your_username -P your_password -t "test"

In a second terminal:

bash复制

mosquitto_pub -h <pi-ip-address> -u your_username -P your_password -t "test" -m "hello"

If the first terminal prints "hello", the broker is working.


Programming the ESP32 to Publish Water Level Data

The ESP32 is the best Arduino-compatible platform for MQTT water level sensing. It has built-in Wi-Fi, a 12-bit ADC, sufficient SRAM for MQTT buffers, and good power management with deep sleep. Any sensor type (ultrasonic, capacitive, eTape, pressure) works with this pipeline - the MQTT code is the same regardless of sensing element.

MQTT Libraries

Install two libraries in the Arduino IDE Library Manager:

PubSubClient by Nick O'Leary - handles the MQTT protocol

NewPing (for HC-SR04) or CapacitiveSensor (for capacitive sensors)

ESP32 MQTT Sketch

cpp复制

#include <WiFi.h> #include <PubSubClient.h> #include <NewPing.h> const char* WIFI_SSID = "YourNetwork"; const char* WIFI_PASS = "YourPassword"; const char* MQTT_BROKER = "192.168.1.100"; // Raspberry Pi IP const int MQTT_PORT = 1883; const char* MQTT_USER = "username"; const char* MQTT_PASS = "password"; const char* MQTT_CLIENT = "esp32-tank"; const char* TOPIC_LEVEL = "home/basement/tank/level"; const char* TOPIC_STAT = "home/basement/tank/status"; #define TRIG_PIN 9 #define ECHO_PIN 10 #define MAX_DIST 200 NewPing sonar(TRIG_PIN, ECHO_PIN, MAX_DIST); const float TANK_HEIGHT_CM = 40.0; // calibrate WiFiClient wc; PubSubClient mqtt(wc); void reconnect() { while (!mqtt.connected()) { if (mqtt.connect(MQTT_CLIENT, MQTT_USER, MQTT_PASS)) { mqtt.publish(TOPIC_STAT, "online"); } else { delay(5000); } } } void setup() { Serial.begin(115200); WiFi.begin(WIFI_SSID, WIFI_PASS); while (WiFi.status() != WL_CONNECTED) delay(500); mqtt.setServer(MQTT_BROKER, MQTT_PORT); } void loop() { if (!mqtt.connected()) reconnect(); mqtt.loop(); unsigned int d = sonar.ping_cm(); if (d == 0) d = MAX_DIST; float pct = constrain((TANK_HEIGHT_CM - d) / TANK_HEIGHT_CM * 100.0, 0, 100); char buf[8]; dtostrf(pct, 4, 1, buf); mqtt.publish(TOPIC_LEVEL, buf); Serial.println(buf); // Deep sleep 5 min to save power; restart on wake WiFi.disconnect(); esp_deep_sleep_start(); }

On deep sleep: esp_deep_sleep_start() resets the ESP32 - constants survive, variables do not. Store calibration values in Preferences (non-volatile) before sleeping. For continuous monitoring, remove the deep sleep lines and use delay() instead.


Configuring Home Assistant MQTT Integration

Step 1 - Add the MQTT integration

In Home Assistant: Settings → Devices & Services → Add Integration → MQTT → Configure

Enter your Mosquitto broker IP address, port 1883, username, and password. Enable discovery if prompted.

Step 2 - Add the water level sensor

In configuration.yaml, add:

yaml复制

sensor: - platform: mqtt name: "Basement Tank Level" state_topic: "home/basement/tank/level" unit_of_measurement: "%" value_template: "{{ value | float }}" availability_topic: "home/basement/tank/status" payload_available: "online" payload_not_available: "offline" qos: 1 - platform: mqtt name: "Basement Tank Sensor Status" state_topic: "home/basement/tank/status"

Reload Home Assistant. The tank level entity appears as sensor.basement_tank_level.

Step 3 - Use Home Assistant Auto-Discovery

If you prefer not to edit YAML, use Home Assistant's MQTT auto-discovery. Have the ESP32 publish a discovery message on boot:

cpp复制

// MQTT auto-discovery payload for Home Assistant const char* DISC_TOPIC = "homeassistant/sensor/basement_tank/config"; const char* DISC_PAYLOAD = "{\"name\":\"Basement Tank Level\"," "\"state_topic\":\"home/basement/tank/level\"," "\"unit_of_measurement\":\"%\"," "\"availability_topic\":\"home/basement/tank/status\"," "\"payload_available\":\"online\"," "\"payload_not_available\":\"offline\"," "\"qos\":\"1\"," "\"device\":{\"identifiers\":[\"esp32_tank_01\"],\"name\":\"Basement Tank Sensor\"}}"; mqtt.publish(DISC_TOPIC, DISC_PAYLOAD);

Home Assistant automatically creates the entity when it receives the discovery message - no YAML editing required.


Building the Dashboard

Home Assistant's dashboard (Lovelace) is fully customizable. Add a gauge card for the tank level:

yaml复制

type: gauge entity: sensor.basement_tank_level name: Basement Tank min: 0 max: 100 unit: "%" severity: green: 20 yellow: 10 red: 5

Add a history graph to see level trends over 24 hours:

yaml复制

type: history-graph entities: - entity: sensor.basement_tank_level name: Tank Level % hours_to_show: 24 refresh_interval: 60

Add a card that shows when the last reading arrived and whether the sensor is online:

yaml复制

type: entities entities: - entity: sensor.basement_tank_level name: Water Level - entity: sensor.basement_tank_sensor_status name: Sensor Status

A live gauge, 24-hour history graph, and status indicator give you the same information as a commercial tank monitoring system for $15 of hardware and free software.


Alert Automations

The real value of MQTT and Home Assistant is automated alerting. Three automations cover the most important water level scenarios.

Low water warning

yaml复制

automation: - alias: "Low water level alert" trigger: - platform: numeric_state entity_id: sensor.basement_tank_level below: 15 action: - service: notify.mobile_app_your_phone data: title: "Low Water Alert" message: "Basement tank is at {{ states('sensor.basement_tank_level') }}% - check refill."

High water flood warning

yaml复制

- alias: "High water flood alert" trigger: - platform: numeric_state entity_id: sensor.basement_tank_level above: 95 action: - service: notify.mobile_app_your_phone data: title: "⚠ Flood Risk" message: "Basement tank at {{ states('sensor.basement_tank_level') }}% - possible overflow." - service: switch.turn_off target: entity_id: switch.sump_pump

Sensor offline notification

yaml复制

- alias: "Sensor offline alert" trigger: - platform: state entity_id: sensor.basement_tank_sensor_status to: "offline" for: minutes: 15 action: - service: notify.mobile_app_your_phone data: title: "Tank Sensor Offline" message: "Basement tank sensor has not reported for 15 minutes. Check power."

All three automations send push notifications through the Home Assistant companion app - no third-party SMS service required.


Extending the System: Multiple Tanks and BLE

Multiple tanks

Add a second ESP32 publishing to a second topic:

code复制

home/garage/tank/level home/garage/tank/status home/roof/tank/level home/roof/tank/status

Home Assistant subscribes to all tanks with a wildcard:

yaml复制

sensor: - platform: mqtt state_topic: "home/+/tank/level" name: "{{ topic.split('/')[1] | title }} Tank Level"

The + wildcard matches any topic level. Home Assistant creates one entity per unique topic - adding a new tank requires only a new ESP32 node, no configuration changes.

BLE sensors

For battery-powered sensors across a property, use a BLE-to-MQTT bridge on the Raspberry Pi. The bridge scans for BLE advertising packets and forwards them to MQTT topics. ESP32 and Nordic nRF52 support BLE advertising mode - wake, read the sensor, broadcast as an advertisement, sleep. A BLE-to-MQTT gateway on the Pi receives the advertisement and publishes it to the broker. Battery life extends to months or years per coin cell.


FAQ: MQTT and Home Assistant for Water Level Sensors

What is the minimum hardware needed?

A single ESP32 ($3–$5) connects to an ultrasonic or capacitive sensor and publishes directly to MQTT over Wi-Fi. The only other requirement is a Raspberry Pi ($15) running Mosquitto and Home Assistant on the same network. Total hardware cost: under $20.

How often should the ESP32 publish?

For slow-changing tanks (level changes over hours), publishing every 5–15 minutes is sufficient and minimizes power consumption. For faster-changing tanks or active filling cycles, 1–5 minute intervals give responsive alerts. Deep sleep between readings on a 5-minute interval gives an ESP32 a battery life of weeks to months.

Does MQTT work over the internet, not just local network?

Yes. Configure your router to forward port 1883 to the Raspberry Pi, and set up a dynamic DNS service (No-IP, DuckDNS) to give your home network a static hostname. Then configure Home Assistant's MQTT integration to connect to yourhostname.ddns.net:1883 from anywhere. Alternatively, use an MQTT broker in the cloud (AWS IoT Core, HiveMQ Cloud) to avoid router port forwarding.

Can I use BLE instead of Wi-Fi for lower power?

Yes. A BLE sensor wakes, takes a reading, broadcasts it as an advertising packet, and goes back to sleep. The Raspberry Pi (or a dedicated BLE gateway ESP32) receives the BLE advertisement and forwards it to MQTT. BLE sensors can run for months to years on a coin cell. For a full BLE-to-MQTT pipeline, use the BLE Advertising mode on ESP32 or Nordic nRF52, or use Xiaomi Mijia BLE sensors with a custom firmware.

How do I log historical data for trend analysis?

Home Assistant's recorder integration stores all entity states in a SQLite database (default) or MariaDB/MySQL (for larger deployments). The history graph in the dashboard shows up to 10 days by default. For longer storage, configure the InfluxDB integration to write all MQTT sensor data to InfluxDB, then use Grafana to build custom trend dashboards over months or years.

My ESP32 keeps disconnecting from Wi-Fi. What can I do?

Check signal strength - the ESP32 Wi-Fi receiver is weak. Move the sensor closer to the access point or use an ESP32 with an external antenna (ESP32-WROOM-DA or ESP32-S3 with U.FL connector). Add a disconnect handler that reboots if reconnection fails:

cpp复制

WiFi.onEvent([](WiFiEvent_t, WiFiEventInfo_t) { Serial.println("Wi-Fi lost, rebooting..."); ESP.restart(); }, SYSTEM_EVENT_STA_DISCONNECTED);

Can I control a pump based on water level?

Yes. Add a relay module to a digital output. Subscribe to a command topic and switch the relay:

cpp复制

const char* CMD_TOPIC = "home/basement/tank/pump/set"; mqtt.subscribe(CMD_TOPIC); void callback(char* topic, byte* payload, unsigned int len) { digitalWrite(PUMP_PIN, strncmp((char*)payload,"ON",len)==0 ? HIGH : LOW); } mqtt.setCallback(callback);

In Home Assistant, add a switch entity publishing to this command topic.

Can I use this system for other sensors too?

The MQTT pipeline is sensor-agnostic. Add temperature (DS18B20), humidity, pressure, or flow rate by publishing to new topics and adding Home Assistant entities. One Raspberry Pi, broker, and Home Assistant instance handles dozens of sensors.


Conclusion

MQTT and Home Assistant transform an Arduino water level sensor from a bench prototype into a production monitoring system. The ESP32 publishes level data to Mosquitto on a Raspberry Pi. Home Assistant subscribes to the topics, stores the history, and drives a dashboard with live gauges and trend graphs. Automations send push notifications when the tank runs low, threatens to overflow, or when the sensor goes offline. The entire stack runs on free open-source software for under $20 in hardware. For multiple tanks, add more ESP32 nodes with unique topics - wildcard subscriptions in Home Assistant create entities automatically. For battery-powered remote tanks, replace Wi-Fi with BLE or LoRa. The foundation is always the same: one ESP32, one sensor, one MQTT topic, one Home Assistant entity. For sensor fundamentals see what is a liquid level sensor; for continuous vs point-level see [continuous_vs_point_level_sensors); for Arduino water level projects see water level sensor Arduino; for sensor types see water level sensor calibration and accuracy; for non-contact options see non-contact liquid level sensors; and the ultimate guide to liquid level switches.

Send Inquiry