Zigbee waterflow meter for Clack valve using ESP32-C6
This is a personal project, based on this tutorial and Ai aid.
I am using this ESP32-C6 Development Kit and Arduino IDE.
Arduino IDE settings:
USB CDC On Boot: "Enabled"
CPU Frequency: "160MHz (WiFi)"
Core Debug Level: "None"
Erase All Flash Before Sketch Upload: "Disabled"
Flash Frequency: "80MHz"
Flash Mode: "QIO"
Flash Size: "4MB (32Mb)"
JTAG Adapter: "Disabled"
Partition Scheme: "Zigbee 4MB with spiffs"
Upload Speed: "115200"
Zigbee Mode: "Zigbee ED (end device)"
It will do the following:
- LED is red prior to connecting to Zigbee, it turns green when connected
- LED will blink when PIN 10 reads some pulses
- Pin 10 will read the pulse from the Clack WS-1 meter and calculate the water usage, unit is Litre
- It will report the data via Zigbee, to be used on Home Assistant
- It will save the reading to the ESP32-C6 board, treated like a real water meter.
Code
#include <Preferences.h>
#include "Zigbee.h"
#define WATER_PIN 10
#define PULSES_PER_LITER 17
#define FLOW_SENSOR_ENDPOINT_NUMBER 10
#define SAVE_INTERVAL 1800000 // 30 minutes in milliseconds
#define REPORT_INTERVAL 60000 // 1 minute in milliseconds
#define ZIGBEE_WATCHDOG_TIMEOUT 7200000 // 2 hours in milliseconds
#define RGB_BRIGHTNESS 50
Preferences preferences;
int pulseCount = 0;
float totalLiters = 0.0;
float lastSavedLiters = 0.0;
bool isLowVoltage = true;
bool isConnected = false;
uint32_t lastReportTime = 0;
uint32_t lastSaveTime = 0;
uint32_t lastZigbeeReportTime = 0;
ZigbeeFlowSensor zbFlowSensor(FLOW_SENSOR_ENDPOINT_NUMBER);
void setLEDColor(uint8_t red, uint8_t green, uint8_t blue) {
#ifdef RGB_BUILTIN
rgbLedWrite(RGB_BUILTIN, red, green, blue);
#endif
}
void saveToNVS() {
preferences.putFloat("totalLiters", totalLiters);
lastSavedLiters = totalLiters;
Serial.printf("💾 Saved to NVS: %.3f L\n", totalLiters);
}
void loadFromNVS() {
totalLiters = preferences.getFloat("totalLiters", 0.0);
lastSavedLiters = totalLiters;
Serial.printf("🔄 Loaded from NVS: %.3f L\n", totalLiters);
}
void setup() {
Serial.begin(115200);
pinMode(WATER_PIN, INPUT_PULLUP);
preferences.begin("water_meter", false);
loadFromNVS();
setLEDColor(RGB_BRIGHTNESS, 0, 0); // Red during setup
zbFlowSensor.setManufacturerAndModel("Espressif", "ZigbeeWaterMeter");
Zigbee.addEndpoint(&zbFlowSensor);
Serial.println("Starting Zigbee...");
if (!Zigbee.begin()) {
Serial.println("❌ Zigbee failed to start! Rebooting...");
delay(100);
ESP.restart();
}
while (!Zigbee.connected()) {
delay(100);
}
isConnected = true;
Serial.println("✅ Zigbee connected!");
setLEDColor(0, RGB_BRIGHTNESS, 0); // Green when connected
zbFlowSensor.setReporting(0, 60, 1.0);
lastReportTime = millis();
lastSaveTime = millis();
lastZigbeeReportTime = millis();
}
void loop() {
uint32_t currentMillis = millis();
int waterValue = digitalRead(WATER_PIN);
if (waterValue == LOW) {
if (isLowVoltage) {
pulseCount++;
isLowVoltage = false;
totalLiters += 1.0 / PULSES_PER_LITER;
Serial.printf("🚿 Water pulse detected! Total: %.3f L\n", totalLiters);
if (isConnected) {
setLEDColor(0, 0, 0); // Blink LED off
delay(100);
setLEDColor(0, RGB_BRIGHTNESS, 0); // Restore green
}
}
} else {
isLowVoltage = true;
}
// Force report every minute
if (currentMillis - lastReportTime >= REPORT_INTERVAL) {
zbFlowSensor.setFlow(totalLiters);
zbFlowSensor.report();
Serial.printf("📢 Forced report (every minute): %.3f L\n", totalLiters);
lastReportTime = currentMillis;
lastZigbeeReportTime = currentMillis;
}
// Save to NVS every 30 minutes if changed
if ((currentMillis - lastSaveTime >= SAVE_INTERVAL) && (totalLiters != lastSavedLiters)) {
saveToNVS();
lastSaveTime = currentMillis;
}
// Restart if no Zigbee report for 2 hours
if (currentMillis - lastZigbeeReportTime >= ZIGBEE_WATCHDOG_TIMEOUT) {
Serial.println("❌ No Zigbee report for 2 hours! Restarting ESP32...");
delay(100);
ESP.restart();
}
}
Add a sensor on configuration.yaml on Home Assistant:
- sensor:
- name: "Total Water Usage"
unique_id: total_water_usage
state: "{{ states('sensor.espressif_zigbeewatermeter') | float(0) }}"
unit_of_measurement: "L"
device_class: water
state_class: total_increasing