ESP8266 - ESP-Now Delivery Failure, but only sometimes

Hi All,

Lurking around in the forum and reddit for some time, though first post here as I am a bit stuck.
I am working on a waterflow sensor to measure a shower waterflow with the goal of telling openHAB if someone is occupying the room or not (to keep lights on/turn them off).
I recently switched, after some Reddit advice, my project to ESP-Now, as without it my 7000mah LifePo4 battery died after 15h.

My setup:

  • Wemos D1 Mini (ESP8266)
  • LifePo4 7000mAh battery connected directly to 3.3V and GND
  • Waterflow sensor (YF-S201) connected to D7 (sensor), D2 (VCC) and GND
  • RST and D0 pins for deepSleep

My issue:
While running the code (see below) I am able to gather the relevant waterflow measure (flowRate) and calculate the totalLiter value.
This also worked (without ESP-Now) reliable, though not battery efficient.
When sending them to the ESP-Now Coordinator though, the communication is only sent/or received (?) sometimes with serial printing "Delivery fail" whereas more often or not it just drops.

My code:

(1) Master/Sensor:

//Include libraries and other includes
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <espnow.h>
#include <SPI.h>

#include "network.h"
#include "waterflow.h"

//Declare GPIO variables - D7 for pulse measure; D2 for VCC
#define SENSOR D7
const int pingPin = D2;
int count = 0;

//Structure example to send data - must match coordinator
typedef struct struct_message {
  float flowRate;
  unsigned long totalLiters;
} struct_message;

//Init struct_messege
struct_message myData;

//Declare functions
//Count pulses
void IRAM_ATTR pulseCounter()
{
  pulseCount++;
}

// Callback when data is sent
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
  Serial.print("Last Packet Send Status: ");
  if (sendStatus == 0){
    Serial.println("Delivery success");
  }
  else{
    Serial.println("Delivery fail");
  }
}
 
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;
  count = 0;

  attachInterrupt(digitalPinToInterrupt(SENSOR), pulseCounter, FALLING);

  //Set device WIFI Station (STA) and ensure it is NOT connected
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();

  // Init ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Once ESP- Now is successfully init, set Role, regirster onDataSent and register with Coordinator - Slave
  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() {
  currentMillis = millis();
  if (currentMillis - previousMillis > interval) {
    pulse1Sec = pulseCount;
    pulseCount = 0;
    flowRate = ((1000.0 / (millis() - previousMillis)) * pulse1Sec) / calibrationFactor;
    previousMillis = millis();
      
    flowMilliLitres = (flowRate / 60) * 1000;
    totalMilliLitres += flowMilliLitres;
    totalLiters = totalMilliLitres / 1000;
      
    if (int(flowRate) < 1) {
      esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(struct_message));
      digitalWrite(pingPin, LOW);
      Serial.println("Going into deep sleep");
      delay(100);
      ESP.deepSleep(15e6);
    } else if (int(flowRate) >= 1) {
      Serial.println(flowRate);
      esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(struct_message));
      return;        
    }
  }
}

(2) Slave/Coordinator:

// Include libraries and other includes
#include <Arduino.h>
#include <ArduinoOTA.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <espnow.h>

#include "network.h"
#include "waterflow.h"

// WIFI and MQTT
WiFiClient espClient;
PubSubClient client(espClient);

// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
  float flowRate;
  unsigned long totalLiters;
} struct_message;

// Create a struct_message called myData
struct_message myData;

// Callback function that will be executed when data is received
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
  //Serial.println("Message received");

  memcpy(&myData, incomingData, sizeof(myData));

  Serial.println(myData.flowRate);
  Serial.println(myData.totalLiters);
  Serial.println(len);
  
  //Translating float to string (_str) and package for mqtt (toCharArray)
  flow_str = String(myData.flowRate);
  flow_str.toCharArray(flow, flow_str.length() + 1);
  total_str = String(myData.totalLiters);
  total_str.toCharArray(total, total_str.length() + 1);

  if (int(myData.flowRate) != 0) {
    Serial.print("Flowrate: ");
    Serial.println(flow);
    client.publish("masterbathroom/shower/flowrate", flow);
  } else if (int(myData.flowRate) == 0) {
    Serial.print("Total Liters: ");
    Serial.println(total);
    client.publish("masterbathroom/shower/totalliters", total);
    client.publish("masterbathroom/shower/state", "OFF");
  }
}
 
void setup() {
  // Init Serial Monitor
  Serial.begin(115200);

  //Set static IP, WIFI mode, connect and loop till connected
  WiFi.mode(WIFI_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);
  }

  //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);
    }
  }

  // Init ESP-Now
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info
  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();
}

void loop() {
  client.loop();
}

As I am just starting out on this journey, I understand that my code might not be the best, it seems to work though to a certain extend.

Another thing I tried was to have the code all only in the setup function with if and while loops to run the calculation so that ESP-Now is only activated once the flowRate value is above "1", whereas I had similar issues as before.

Could anyone point me into the right direction on how to fix this issue?

Hello,

I'm not sure if it will help but someone had a similar problem recently :

https://forum.arduino.cc/t/espnow-fails-to-work-with-deepsleep/703113

interesting.

How did you manage to have "normal" WiFi and ESP-NOW on a ESP8266 in parallel?

As far as I know this is not possible on ESP8266.
an ESP32 can do it as long as ESP-NOW uses the same channel as the WiFi.

Anyway: WiFi needs quite some current to work. So running an application where the microcontroller has to run all the time (for measuring waterflow)
an ESP needs quite some energy.

A microcontroller with low power-consumption would be much better suited.

If the device shall run on a battery. Applications that can fall asleep for a minute then wake up send a message for 0,1 seconds fall asleep again
might do on an ESP with a battery.

Otherwise use a wall-plug and accept a power-consumption of 1W 24/7
= 9kWh per Year.

Or use a different wireless technolgy or combine a low-power-CPU measuring waterflow with an ESP where the LOW-power-CPU wakes up the ESP once per minute to transfer the data wireless.

best regards Stefan

He, sorry for my late reply and thanks for your comment.
Did not have much time over the last days to work on the ESP8266 project, whereas I found out that it indeed is somehow connected to the WiFi channel (i.e. my AP was using channel 6, whereas ESP-Now supposedly only (?) works on 1).

I wrote some test code and used the following WiFi and ESP-Now config which in a first test seemed to work, at least all deliveries were a success.

My test code for reference below.

Coordinator:

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <espnow.h>
//#include <PubSubClient.h>

#include "network.h"

// WIFI and MQTT
//WiFiClient espClient;
//PubSubClient client(espClient);

// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
    char a[32];
    int b;
    float c;
    String d;
    bool e;
} struct_message;

// Create a struct_message called myData
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));
  Serial.print("Bytes received: ");
  Serial.println(len);
  Serial.print("Char: ");
  Serial.println(myData.a);
  //client.publish("masterbathroom/shower/a", myData.a);
  Serial.print("Int: ");
  Serial.println(myData.b);
  
  char myDatab[32];
  String myDatab_str;
  myDatab_str = String(myData.b);
  myDatab_str.toCharArray(myDatab, myDatab_str.length() + 1);
  
  //client.publish("masterbathroom/shower/b", myDatab);
  Serial.print("Float: ");
  Serial.println(myData.c);
  Serial.print("String: ");
  Serial.println(myData.d);
  Serial.print("Bool: ");
  Serial.println(myData.e);
  Serial.println();
}
 
void setup() {
  // Initialize Serial Monitor
  Serial.begin(115200);
  
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_AP_STA);

  // Set device as a Wi-Fi Station
  WiFi.begin(ssid, password, channel, bssid);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Setting as a Wi-Fi Station..");
  }

  Serial.print("Station IP Address: ");
  Serial.println(WiFi.localIP());
  Serial.print("Wi-Fi Channel: ");
  Serial.println(WiFi.channel());
  
  //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);
  //}

  //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);
  //  }
  //}

  // Init ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  
  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info
  esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
  esp_now_register_recv_cb(OnDataRecv);
}

void loop() {
  //client.loop();
}

Sender:

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <espnow.h>

// REPLACE WITH RECEIVER MAC Address
uint8_t broadcastAddress[] = {0x84, 0xCC, 0xA8, 0xA6, 0x1D, 0x7D};
uint8_t bssid[] = {0x18,0xE8,0x29,0xCC,0xF0,0x4E};

// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
  char a[32];
  int b;
  float c;
  String d;
  bool e;
} struct_message;

// Create a struct_message called myData
struct_message myData;

unsigned long lastTime = 0;  
unsigned long timerDelay = 2000;  // send readings timer
unsigned int readingId = 0;

// Insert your SSID
constexpr char WIFI_SSID[] = "IOT";

int32_t getWiFiChannel(const char *ssid) {
  if (int32_t n = WiFi.scanNetworks()) {
    for (uint8_t i=0; i<n; i++) {
      if (!strcmp(ssid, WiFi.SSID(i).c_str())) {
        return WiFi.channel(i);
      }
    }
  }
  return 0;
}

// Callback when data is sent
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
  Serial.print("Last Packet Send Status: ");
  if (sendStatus == 0){
    Serial.println("Delivery success");
  }
  else{
    Serial.println("Delivery fail");
  }
}
 
void setup() {
  // Init Serial Monitor
  Serial.begin(115200);
 
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);
  int32_t channel = getWiFiChannel(WIFI_SSID);

  WiFi.printDiag(Serial); // Uncomment to verify channel number before
  wifi_promiscuous_enable(1);
  wifi_set_channel(channel);
  wifi_promiscuous_enable(0);
  WiFi.printDiag(Serial); // Uncomment to verify channel change after

  // Init ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of Trasnmitted packet
  esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
  esp_now_register_send_cb(OnDataSent);
  
  // Register peer
  esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
}
 
void loop() {
  if ((millis() - lastTime) > timerDelay) {
    // Set values to send
    strcpy(myData.a, "THIS IS A CHAR");
    myData.b = random(1,20);
    myData.c = 1.2;
    myData.d = "Hello";
    myData.e = false;

    // Send message via ESP-NOW
    esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));

    lastTime = millis();
  }
}

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.