I followed some online how-to’s and github codes to come up with an implementation.
The equipment I am using for now:
- Wemos D1 Mini
- LifePo4 7000mAh battery connected to 3.3v and GND (I don’t have a power outlet close by)
- Waterflow Sensor YF201 linked to GND, D7 (data) and D2 (VCC)
- deepSleep set via RST and D0 short
My initial non-ESP-Now code is here on reddit.
Since then I changed to ESP-Now though which requires 2 devices (a receiver to connect to WiFi and MQTT and one sender which has the sensor connected).
My code for both is below, take into account that it might not be fully optimzied:
Coordinator:
//Include libraries and other includes
#include <Arduino.h>
#include <ArduinoOTA.h>
#include <ESP8266WiFi.h>
#include <espnow.h>
#include <PubSubClient.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include "network.h"
#include "waterflow.h"
#include "ntp.h"
//Init WiFi and MQTT
WiFiClient espClient;
PubSubClient client(espClient);
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "asia.pool.ntp.org", utcOffsetInSeconds);
//Structure to receive data and create myData
typedef struct struct_message {
int status;
int totalLiters;
float flowRate;
} struct_message;
struct_message myData;
//Callback function that will be executed when data is received
void OnDataRecv(uint8_t *mac, uint8_t *incomingData, uint8_t len) {
memcpy(&myData, incomingData, sizeof(myData));
if (int(myData.flowRate) >= 1) {
//Calculate flowRate as string for MQTT
flow_str = String(myData.flowRate);
flow_str.toCharArray(flow, total_str.length() + 5);
client.publish("masterbathroom/shower/flowrate", flow);
client.loop();
} else {
//Calculate totalliters as string for MQTT
total_str = String(myData.totalLiters);
total_str.toCharArray(total, total_str.length() + 5);
//Calculate timestamp - epochTime for MQTT last_seen
timeClient.update();
unsigned long epochTime = timeClient.getEpochTime();
epochTime_str = String(epochTime);
epochTime_str.toCharArray(epochTime_mqtt, epochTime_str.length() + 1);
//Serial.print("Total Liters: ");
//Serial.println(total);
client.publish("masterbathroom/shower/last_seen", epochTime_mqtt);
client.publish("masterbathroom/shower/totalliters", total);
client.publish("masterbathroom/shower/state", "OFF");
client.loop();
}
}
void setup() {
//Initialize Serial Monitor
Serial.begin(115200);
//Set device as WiFi station - loop till connected
WiFi.mode(WIFI_AP_STA);
WiFi.config(staticIP, dns, gateway, subnet);
WiFi.begin(ssid, password);
//, channel, bssid);
//WiFi.persistent(true);
//WiFi.setAutoConnect(true);
//WiFi.setAutoReconnect(true);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("Setting as a Wi-Fi Station..");
}
//Connect to MQTT - loop till connected
client.setServer(mqttServer, mqttPort);
while (!client.connected()) {
if (client.connect("WEMOS", mqttUser, mqttPassword)) {
client.subscribe("masterbathroom/shower");
} else {
delay(500);
Serial.println("Connecting to MQTT..");
}
}
//Init ESP-Now
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}
//Once ESPNow is successfully init, set ESP-Now role and register recv
esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
client.publish("masterbathroom/shower/state_coordinator", "ON");
esp_now_register_recv_cb(OnDataRecv);
//Start OTA mode
ArduinoOTA.onStart([]() {
Serial.println("Start");
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR)
Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR)
Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR)
Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR)
Serial.println("Receive Failed");
else if (error == OTA_END_ERROR)
Serial.println("End Failed");
});
ArduinoOTA.begin();
timeClient.begin();
}
void loop()
{
client.loop();
}
Sender:
//Include libraries and other includes
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <espnow.h>
#include <SPI.h>
#include "waterflow.h"
//Declare GPIO variables - D7 for pulse measure; D2 for VCC
#define SENSOR D7
const int pingPin = D2;
int status = 0;
//Receiver MAC-Address
uint8_t broadcastAddress[] = {0x84, 0xCC, 0xA8, 0xA6, 0x1D, 0x7D};
//Structure example to send data - must match coordinator
typedef struct struct_message {
int status;
float flowRate;
} struct_message;
// Create a struct_message called myData
struct_message myData;
//Declare functions
//Count pulses of the waterflow sensor
void IRAM_ATTR pulseCounter()
{
pulseCount++;
}
//Callback when data is sent
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
if (sendStatus == 0){
} else {
}
if (int(flowRate) == 0) {
ESP.deepSleep(240e6);
}
}
void setup() {
//Init Serial Monitor
//Serial.begin(115200);
//Deactive build-in LED and power up GPIO D2
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, HIGH);
pinMode(SENSOR, INPUT_PULLUP);
//Set warterflow variables
pulseCount = 0;
flowRate = 0.0;
flowMilliLitres = 0;
totalMilliLitres = 0;
previousMillis = 0;
totalLiters = 0;
attachInterrupt(digitalPinToInterrupt(SENSOR), pulseCounter, FALLING);
//Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
int32_t channel = 1;
wifi_promiscuous_enable(1);
wifi_set_channel(channel);
wifi_promiscuous_enable(0);
WiFi.disconnect();
//Init ESP-NOW
if (esp_now_init() != 0) {
return;
}
//Once ESP-Now is successfully init, set role, regirster onDataSent and register with Coordinator
esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
esp_now_register_send_cb(OnDataSent);
esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
}
void loop() {
//Init struct_messege
myData.status = status;
myData.flowRate = flowRate;
currentMillis = millis();
if (currentMillis - previousMillis > interval) {
pulse1Sec = pulseCount;
pulseCount = 0;
flowRate = ((1000.0 / (millis() - previousMillis)) * pulse1Sec) / calibrationFactor;
previousMillis = millis();
if (int(flowRate) < 5) {
status = 0;
esp_now_send(broadcastAddress, (uint8_t *)&myData, sizeof(struct_message));
digitalWrite(pingPin, LOW);
} while (int(flowRate) > 5) {
status = 1;
esp_now_send(broadcastAddress, (uint8_t *)&myData, sizeof(struct_message));
return;
}
}
}
Open to code improvements if someone has further ideas.