Hello Fellow Nerds.
I have taken the latest work from Rui using the ESP8266 using ESPNOW and slightly modified it to do what I need. My project scope is to monitor water and fuel usage on my sailboat. So two water tanks and one fuel tank. I am some success with two communication sending commands from the main display monitor to one of the tank sensor modules. But as you can hopefully see in the serial monitor display printouts below, there are many missing packets of data. I do not understand why it is so hit and miss. I am sure I am doing something wrong, and I hope you guys can help point it out to me. My head huts from banging it on my desk, Lol..
-
Master display monitor code here::
/* WiFi Fluid Level Monitor using ESP8266 NodeMCU Module.
*
*
*
*
*
*/
#include
#include
const int refreshTime = 7000;
const int heartBeatRate = 1000; // Blink heartbeat at 0.5Hz.
const int heartbeatPin = 16; //LED_BUILTIN;
const int WiFi_XmitLED = 2;
unsigned long currentMillis = 0;
unsigned long heartBeatMillis = 0;
unsigned long commandMillis = 0;
int loopCount = 0;
String command;
boolean heartbeatState = false;
uint8_t broadcastAddressDisplay[] = {0xA4, 0xCF, 0x12, 0xD9, 0x7B, 0x95}; // ESP8266 NodeMCU #1
uint8_t broadcastAddressPortTank[] = {0xA4, 0xCF, 0x12, 0xD9, 0x7B, 0x74}; // ESP8266 NodeMCU #2
uint8_t broadcastAddressStbdTank[] = {0xA4, 0xCF, 0x12, 0xD9, 0x7B, 0x53}; // ESP8266 NodeMCU #3
// Structure of receive data. Must match the sender's structure:
typedef struct struct_message {
String location;
float flowSecond;
float flowTotal;
float fluidTemp;
} struct_message;
// Create a struct_message called sensorData:
struct_message sensorData;
// Structure of command data. Must match the receiver's structure:
typedef struct struct_command {
String location;
String command;
int ID;
} struct_command;
// Create a struct_message called sensorData:
struct_command sensorCommand;
/**************************************************************
* Callback function when data is received:
*/
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len)
{
memcpy(&sensorData, incomingData, sizeof(sensorData));
Serial.print("Bytes received: ");
Serial.println(len);
Serial.print("Location: ");
Serial.println(sensorData.location);
Serial.print("Flow/Sec.: ");
Serial.println(sensorData.flowSecond);
Serial.print("Flow/total: ");
Serial.println(sensorData.flowTotal);
Serial.print("Fluid Temp.: ");
Serial.println(sensorData.fluidTemp);
Serial.println();
} // End of OnDataRecv()
/*************************************************************
* Callback function when data is sent:
*/
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus)
{
Serial.print("Last Packet Send Status: ");
if (sendStatus == 0){
Serial.println("ID = " + String(loopCount) + ":: Delivery success");
}
else{
Serial.println("ID = " + String(loopCount) + ":: Delivery fail. Status = " + String(sendStatus));
}
} // End of OnDataSent()
/************************************************************
* Setup the system so we can get to work:
*/
void setup()
{
digitalWrite(heartbeatPin, LOW);
pinMode(heartbeatPin, OUTPUT);
digitalWrite(WiFi_XmitLED, HIGH);
pinMode(WiFi_XmitLED, OUTPUT);
// Initialize Serial Monitor
Serial.begin(115200);
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
// 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_register_send_cb(OnDataSent);
// // Registering peers need to change into a function once I learn how to do it...
// // Register peer
// esp_now_peer_info_t peerInfo;
// memcpy(peerInfo.peer_addr, broadcastAddressPortTank, 6);
// peerInfo.channel = 0;
// peerInfo.encrypt = false;
//
// // Add peer
// if (esp_now_add_peer(&peerInfo) != ESP_OK){
// Serial.println("Failed to add peer");
// return;
// }
//
// // Register peer
// esp_now_peer_info_t peerInfo;
// memcpy(peerInfo.peer_addr, broadcastAddressStbdTank, 6);
// peerInfo.channel = 1;
// peerInfo.encrypt = false;
//
// // Add peer
// if (esp_now_add_peer(&peerInfo) != ESP_OK){
// Serial.println("Failed to add peer");
// return;
// }
// //--------------------------------------------------------------------------------
// Register for a callback function that will be called when data is received
esp_now_register_recv_cb(OnDataRecv);
// Once ESPNow is successfully Init, we will register for recv CB to
// get recv packer info
esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
// Register peers
esp_now_add_peer(broadcastAddressPortTank, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
//esp_now_add_peer(broadcastAddressStbdTank, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
} // End of setup()
/****************************************************************************
*
*/
void loop()
{
currentMillis = millis();
if(currentMillis - commandMillis >= refreshTime) {
commandMillis = currentMillis;
digitalWrite(WiFi_XmitLED, LOW);
sensorCommand.location = "Port Tank";
sensorCommand.command = "@PT#"; // Get port tank status.
sensorCommand.ID = loopCount;
// Send message via ESP-NOW
Serial.println("\nCommand just sent...");
esp_now_send(broadcastAddressPortTank, (uint8_t *) &sensorCommand, sizeof(sensorCommand));
digitalWrite(WiFi_XmitLED, HIGH);
loopCount++;
if(loopCount >= 10000) {
loopCount = 0;
}
}
// Blink Heartbeat LED
currentMillis = millis();
if(currentMillis - heartBeatMillis > heartBeatRate) {
heartBeatMillis = currentMillis;
if(heartbeatState) {
//digitalWrite(heartbeatPin, LOW);
heartbeatState = false;
}
else {
//digitalWrite(heartbeatPin, HIGH);
heartbeatState = true;
}
digitalWrite(heartbeatPin, heartbeatState ? HIGH : LOW);
}
} // End of loop()
-
Here is the sensor slave code:
/* WiFi Fluid Sensor Transmitter using ESP8266 NodeMCU Module.
*
*
*/
#include
#include
#define DEBUG
const int dataRateUpdate = 5000;
const int heartBeatRate = 1000; // Blink heartbeat at 0.5Hz.
const int heartbeatPin = 16; //LED_BUILTIN;
const int WiFi_XmitLED = 2;
const int flowsensor = 5; // Sensor Input
const int tempSensor = A0;
volatile int flow_frequency; // Measures flow sensor pulses
unsigned long currentTime = 0;
unsigned long flowTime = 0;
unsigned long tempTime = 0;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
unsigned long dataMillis = 0;
boolean heartbeatState = false;
String flowData;
int dataLength = 0;
int tempSensorVal = 0;
float rawTemp = 0;
float tempDegreesF = 0;
// Define variables to store flow and temp readings to be sent:
float degreesF = 0;
float flowSecond = 0;
float flowTotal = 0;
// REPLACE WITH RECEIVER MAC Address
uint8_t broadcastAddressDisplay[] = {0xA4, 0xCF, 0x12, 0xD9, 0x7B, 0x95}; // ESP8266 NodeMCU #1
//uint8_t broadcastAddressPortTank[] = {0xA4, 0xCF, 0x12, 0xD9, 0x7B, 0x74}; // ESP8266 NodeMCU #2
//uint8_t broadcastAddressStbdTank[] = {0xA4, 0xCF, 0x12, 0xD9, 0x7B, 0x53}; // ESP8266 NodeMCU #3
// Structure of send data. Must match the receiver structure
typedef struct struct_message {
String location;
float flowSecond;
float flowTotal;
float fluidTemp;
} struct_message;
// Create a struct_message called myData
struct_message sensorData;
// Structure of command data. Must match the receiver's structure:
typedef struct struct_command {
String location;
String command;
int ID;
} struct_command;
// Create a struct_message called sensorData:
struct_command sensorCommand;
/**************************************************************
* Callback function when data is received:
*/
void OnDataRecv(uint8_t * mac, uint8_t *incomingCommand, uint8_t len)
{
memcpy(&sensorCommand, incomingCommand, sizeof(sensorCommand));
Serial.print("Bytes received: ");
Serial.println(len);
Serial.print("Location: ");
Serial.println(sensorCommand.location);
Serial.print("Command: ");
Serial.println(sensorCommand.command);
Serial.print("ID: ");
Serial.println(sensorCommand.ID);
Serial.println('\n');
sendData();
} // End of OnDataRecv()
/*************************************************************
* Callback function 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\n\n");
}
else{
Serial.println("Delivery fail. Status = " + String(sendStatus) + "\n\n");
}
}
/*****************************************************************
* Interrupt routine for water flow sensor
* Interrupt rate is 29.5735 pulses/ounce.
*/
//ESP_INTR_FLAG_IRAM sensorISR()
void IRAM_ATTR sensorISR()
{
flow_frequency++;
}
/**************************************************************
* Setup all things needed:
*/
void setup()
{
digitalWrite(heartbeatPin, LOW);
pinMode(heartbeatPin, OUTPUT);
pinMode(flowsensor, INPUT_PULLUP);
digitalWrite(WiFi_XmitLED, HIGH);
pinMode(WiFi_XmitLED, OUTPUT);
// Init Serial Monitor
Serial.begin(115200);
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
// Init ESP-NOW
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}
// Once ESPNow is successfully Init, register for Send and receive callback to get the status of Trasnmitted packet
esp_now_register_send_cb(OnDataSent);
esp_now_register_recv_cb(OnDataRecv);
// Set ESP module's role
esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
// Register peer
//esp_now_add_peer(broadcastAddressDisplay, ESP_NOW_ROLE_CONTROLLER, 1, NULL, 0);
attachInterrupt(digitalPinToInterrupt(flowsensor), sensorISR, RISING); // Setup Interrupt
interrupts();
} // End of setup()
int Idx = 0;
void loop()
{
// Every second, calculate and send data:
currentTime = millis();
if(currentTime >= (flowTime + 1000)) {
flowTime = currentTime; // Updates loopTime
// Calculate water flow.
flowSecond = flow_frequency / 3785.412;
flowTotal += flowSecond;
flow_frequency = 0; // Reset Counter
// Clear string so new data will take it's place, not be added onto it.
dataLength = flowData.length();
flowData.remove(0, dataLength);
}
// Read temp sensor every 15 seconds:
currentTime = millis();
if(currentTime >= (tempTime + 15000)) {
tempTime = currentTime; // Updates tempTime
tempSensorVal = analogRead(tempSensor); // Read the analog sensor.
rawTemp = (float)tempSensorVal / 4096 * 3.3; // ESP32 has 12 bit AD, ESP8266 is 10 bit:Find percentage of input reading.
//rawTemp = rawTemp * 3.3; // Multiply by 3.3V to get voltage
//rawTemp = rawTemp - 0.5; // Subtract the offset.
rawTemp = rawTemp * 100; // Convert to degrees.
tempDegreesF = (rawTemp * 1.8) + 32;
}
// Blink Heartbeat LED:
currentMillis = millis();
if(currentMillis - previousMillis > heartBeatRate) {
previousMillis = currentMillis;
if(heartbeatState) {
digitalWrite(heartbeatPin, LOW);
heartbeatState = false;
}
else {
digitalWrite(heartbeatPin, HIGH);
heartbeatState = true;
//#ifdef DEBUG
// Serial.println("Flow/Sec. = " + String(flowSecond) + " :: Flow/Total = " + String(flowTotal) + " :: Fluid Temp = " + String(tempDegreesF));
//#endif
}
}
} // End of loop()
/********************************************************
* Send Sensor data to main display:
*/
void sendData()
{
digitalWrite(WiFi_XmitLED, LOW);
sensorData.location = "Port Tank";
sensorData.flowSecond = flowSecond;
sensorData.flowTotal = flowTotal;
sensorData.fluidTemp = tempDegreesF;
// Send flow data via WiFi to main display.
flowData.concat("@");
flowData.concat(flowSecond);
flowData.concat("::");
flowData.concat(flowTotal);
flowData.concat("::");
flowData.concat(tempDegreesF);
flowData.concat("#");
#ifdef DEBUG
Serial.println(flowData);
#endif
// Send message via ESP-NOW to main display:
esp_now_send(broadcastAddressDisplay, (uint8_t *) &sensorData, sizeof(sensorData));
//Serial.println("Data just sent..");
digitalWrite(WiFi_XmitLED, HIGH);
}
-
The output from Master is below.
Last Packet Send Status: ID = 111:: Delivery fail. Status = 1
Command just sent…
Last Packet Send Status: ID = 112:: Delivery fail. Status = 1
Bytes received: 24
Location: Port Tank
Flow/Sec.: 0.99
Flow/total: 3514.75
Fluid Temp.: 64.05
Command just sent…
Last Packet Send Status: ID = 113:: Delivery fail. Status = 1
Command just sent…
Last Packet Send Status: ID = 114:: Delivery fail. Status = 1
Command just sent…
Last Packet Send Status: ID = 115:: Delivery success
Bytes received: 24
Location: Port Tank
Flow/Sec.: 0.99
Flow/total: 3535.56
Fluid Temp.: 63.76
Command just sent…
Last Packet Send Status: ID = 116:: Delivery fail. Status = 1
-
And on the sensor side I see this.
Bytes received: 28
Location: Port Tank
Command: @PT#
ID: 111
@0.99::3514.75::64.05#
Last Packet Send Status: Delivery fail. Status = 1
Bytes received: 28
Location: Port Tank
Command: @PT#
ID: 114
@0.99::3535.56::63.76#
Last Packet Send Status: Delivery success
Hi Steve,
It’s not easy to read your code that way… In the future you might want to consider posting it on GitHub Gist 🙂
Well, I haven’t tested it (I haven’t had time to experiment with the ESP-NOW protocol yet), but at first glance I have the impression that the problem comes from the combination of using memcpy
with sizeof
on a struct
inside your OnDataRecv
function (Master side):
memcpy(&sensorData, incomingData, sizeof(sensorData));
Take a look at this, you’ll get a better idea of what I’m talking about.
I’d rather you didn’t do too much damage to your head! 😀
Hi Stephane,
I agree my code is hard to read the way I posted. Being new to this forum I did not know how to post and have it look presentable. I also didn’t want to just try things and end up with a bunch of garbage posts if I could not edit or remove practice posting.
I will look into the GitHub Gist as you suggest. Thanks. I will also look at your suggestion, but I am pretty sure I copied the memcpy command right from Rui’s example.
Thank you for your help. I will post my findings later today. I really want this working before the weekend is over.Â
Regards,
Steve
Yeah, well, the micro-editor isn’t great for formatting our posts. I had a hard time myself at first… and now I often use the Text tab (in the upper right corner) to directly edit the post’s HTML code to make sure it appears the way I want it to.
I’m not going to be able to get into the problem right away… it’s 2:00 AM at my place (Reunion Island) and I really need to get some sleep if I’m going to be able to study the ESP-NOW protocol tutorial… but I’m pretty sure the problem is what I suggested…
I’ll try to look at it in more detail later, after I get some sleep… I have to admit, my neurons need it right now.
Hi Stephane,
Well it took me most of the weekend but I figured it out. I did take time out to play with my grandkids to clear my head. That didn’t help but it was fun. Anyway, it seems that because I have tried numerous examples with these modules, and one of them being WiFi, that somehow the WiFi was still connected in some way. So this was causing some kind of interference. I do not know why. While searching for any clue I could find I saw someone’s “Hello World” example and in there they used WiFi.disconnect(). That was it!!!! Now I am sending commands from one unit, and the slave unit responds as it should. Oh happy day!!!
Regards,
Steve.