I am running Esp NOW with sequential board communication.
The problem is sequencing board events upon the receipt of a message.
It is a simple timeline and illustrated below:
Receive message
Turn on Led
Wait 100ms
Replicate and send incoming message to next unit
5 Wait 400ms (or whatever balance time for Led on)
Turn Led off
Wait until new message received and start again (approx 1500ms intervals)
I have tried a number of approaches with millis , case and even delay.
I cant get one to work consistently.
the common problem (when it does work) is that about 20% of the Led time on periods appear to be interfered with and inconsistent.(abrupt flash only)
I am sure there is a simple solution but just can’t see the forest for the Trees at the moment.
// upon receipt - digital write as above
// delay 100ms and send myData to next peer
// further delay to turn Led off (total Led on time say 500ms)
// wait for next incoming message
}
How do you know this is the relevant part?
Don’t post snippets (Snippets R Us!)
—-
please edit your post, select the code part and press the </> icon in the tool bar to mark it as code. It’s painful to read as it stands. (also make sure you indented the code in the IDE before copying, that’s done by pressing ctrlT on a PC or cmdT on a Mac)
I suspect the issue is more to do with managing the incoming data to be acted upon initially.
In this case a simple “0” .
Separating it out to another variable and acting on that with if .. does not work either.
/*
ESP NOw 8266
Receive data and act on it
Havent got it to work with Scheduler or millis
works with delay sort of
Forwarding-on bits removed until receiving actions sorted.
*/
#include <ESP8266WiFi.h>
#include <espnow.h>
#include <SchedTask.h> //scheduling elements removed
int ledPin = 2;
uint8_t pinStatus = 1;
// Structure of receive data
typedef struct message {
uint8_t pinStatus;
} message;
message myData; // message called 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.print (len);
Serial.println ();
Serial.print("Pin Status is : ");
Serial.println(myData.pinStatus);
digitalWrite (ledPin, myData.pinStatus); //turns Led on - works (but cant turn off)
//Turning Led off and subsequent operations to follow
}
void setup() {
Serial.begin(115200);
pinMode (ledPin, OUTPUT);
WiFi.mode(WIFI_STA); // Set device as a Wi-Fi Station
WiFi.disconnect();
// Init ESP-NOW
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}
// get recv packer info
esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
esp_now_register_recv_cb(OnDataRecv);
}
void loop() {
//using delay and off-Led here sort of works not properly & stuffs up onforwarding
}
The code yu have posted does just receive some data and that's it.
No non-blocking timing no sending.
if you want to receive ESP-NOW-Data you have to avoid delay()
The receiver can not predict when a new ESP-NOW-message arrives
Delay() blocks the complete processor. So the unit is unable to receive anything as long as the processor is executing a delay().
Your code does include a library taskScheduler but your code does not use it.
I have never used TaskScheduler. I always use non-blocking timing based on millis(). To make timing based on millis() really non-blocking it has to be done in a certain way.
as an allday example with easy to follow numbers
delay() is blocking. As long as the delay is "delaying" nothing else of the code can be executed.
Now there is a technique of non-blocking timing.
The basic principle of non-blocking timing is fundamental different from using delay()
You have to understand the difference first and then look into the code.
otherwise you might try to "see" a "delay-analog-thing" in the millis()-code which it really isn't
Trying to see a "delay-analog-thing" in millis() makes it hard to understand millis()
Having understood the basic principle of non-blocking timing based on millis() makes it easy to understand.
imagine baking a frosted pizza
the cover says for preparation heat up oven to 200°C
then put pizza in.
Baking time 10 minutes
You are estimating heating up needs 3 minutes
You take a look onto your watch it is 13:02 (snapshot of time)
You start reading the newspaper and from time to time looking onto your watch
watch shows 13:02. 13:02 - 13:02 = 0 minutes passed by not yet time
watch shows 13:03. 13:03 - 13:02 = 1 minute passed by not yet time
watch shows 13:04. 13:04 - 13:02 = 2 minutes passed by not yet time
watch shows 13:05 when did I start 13:02? OK 13:05 - 13:02 = 3 minutes time to put pizza into the oven
New basetime 13:05 (the snapshot of time)
watch 13:06 not yet time
watch 13:07 not yet time
watch 13:08 not yet time (13:08 - 13:05 = 3 minutes is less than 10 minutes
watch 13:09 not yet time
watch 13:10 not yet time
watch 13:11 not yet time
watch 13:12 not yet time
watch 13:13 not yet time
watch 13:14 not yet time (13:14 - 13:05 = 9 minutes is less than 10 minutes
watch 13:15 when did I start 13:05 OK 13:15 - 13:05 = 10 minutes time to eat pizza (yum yum)
You did a repeated comparing how much time has passed by
This is what non-blocking timing does
In the code looking at "How much time has passed by" is done
currentTime - startTime >= bakingTime
bakingTime is 10 minutes
13:06 - 13:05 = 1 minute >= bakingTime is false
13:07 - 13:05 = 2 minutes >= bakingTime is false
...
13:14 - 13:05 = 9 minutes >= bakingTime is false
13:15 - 13:05 = 10 minutes >= bakingTime is TRUE time for timed action!!
So your loop() is doing
void loop()
// doing all kinds of stuff like reading the newspaper
if (currentTime - previousTime >= period) {
previousTime = currentTime; // first thing to do is updating the snapshot of time
// time for timed action
}
it has to be coded exactly this way because in this way it manages the rollover from max back to zero
of the function millis() automatically
baldengineer.com has a very good tutorial about timing with function millis() too .
There is one paragraph that nails down the difference between function delay() and millis() down to the point:
The millis() function is one of the most powerful functions of the Arduino library. This function returns the number of milliseconds the current sketch has been running since the last reset. At first, you might be thinking, well that’s not every useful! But consider how you tell time during the day. Effectively, you look at how many minutes have elapsed since midnight. That’s the idea behind millis()!
Instead of “waiting a certain amount of time” like you do with delay(), you can use millis() to ask “how much time has passed”?
Stefan,
Thank you for your time and the effort of your response.
(I have read number of your posts).
I just have a mental block on this one
I am familiar with the millis() timing and have tried it with this sketch both in the main loop and as a subroutine
No joy.
The timing is sorted on the ‘Master’ sketch (using a scheduler) which works well and the current permutation is sending timed instruction for both led on and off to the slave. So just now trying to send the data onward with a delay before doing so. The intent is a cascade effect for subsequent units.
My issue is perhaps not so much a timing problem but setting the threshold that subsequent instructions act upon once the data is received at the slave.
Attached is the latest slave sketch. I have tried various placements of a forwarding instruction and methods to address it but without success, so something basic is wrong.
The message is forwarded but the timing aspect appears to be ignored and the data is sent immediately upon receipt. (I tried different timings in case of inbuilt latency).
I intially had the same problem trying to control the led locally (but resolved that via another method).
Fix that and I suspect the rest will just follow, so any pointers on that will be appreciated.
/*
ESP 8266 ESP NOW station 2 sending to 3
Data recieving works
send works but 'delay' does not
*/
#include <ESP8266WiFi.h>
#include <espnow.h>
int ledPin = 2;
void Send();
unsigned long lastTime = 0;
unsigned long Wait = 100;
// RECEIVER MAC Address
uint8_t broadcastAddress[] = {0x8C, 0xAA, 0xB5, 0x7B, 0x4D, 0x1E}; // No 3 address
// Structure of data
typedef struct message {
int pinStatus;
} message;
// message
message myData;
// 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");
}
}
// 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));
digitalWrite (ledPin, myData.pinStatus); //change pin status
Serial.print("Bytes received: ");
Serial.print (len);
Serial.println ();
Serial.print("Pin Status is : ");
Serial.println(myData.pinStatus); //audit incoming data
Send();
}
void setup() {
// Initialize Serial Monitor
Serial.begin(115200);
pinMode (ledPin, OUTPUT);
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
WiFi.disconnect();
// Init ESP-NOW
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}
esp_now_set_self_role(ESP_NOW_ROLE_COMBO);
esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_COMBO, 1, NULL, 0); // Register peer
esp_now_register_send_cb(OnDataSent);
esp_now_register_recv_cb(OnDataRecv);
}
//..................................................................
void loop() {
}
//..................................................................
void Send () {
if ((millis() - lastTime) > Wait) {
// Send message
esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
lastTime = millis();
}
}
I assume the scheduler takes action at a fixed intervall.
This means if you use a a scheduler inside the slaves that acts on a fixed time-interval the slaves will drift away over time.
Your slaves should act not on a purely "fixed-timed" condition but on a condition like "command LED on received" This command will start the wait-time
The state-machine that J-M-LJackson has described will do the job.
I looked into your code
The function void OnDataRecv is called only once per receved message
you call your function Send() inside OnDataRecv
This means you call the function Send() only once
and never again
but your construction of function Send()
needs to be called again and again to finally see the condition to become true
This would only work if you would send a lot of messages always with the command LED on or LED off in a fast sequence to keep the checking of
if ((millis() - lastTime) > Wait) going.
As a general hint:
Add serial-debug-output to your code and watch the serial output
If you would have added serial output inside your Send()-function
you would have seen in the serial-monitor Send() gets called only once
and this would have guided you to the place to look at.
I do not use a scheduler in the slave.
I have adjusted the set up to the sequence as indicated by J-L-M. It hasnt change any outcome so far.
I will add a debugger to the loop and check that but suspect it will show what you have indicated.
The 'Send' function is suppose to only operate once upon each message receipt - it does this but the delay period part doesn't work .
i understand the dilemma of needing the timing part to be in a continuing loop for the millis() to work.
the logic of a state machine i also understand and agree it is approriate but perhaps more complex than needed here.
As the Send fn timing and base line isnt working;
how do i do the recomemded:
memorise millis
set the flag for next activity.
This is where i am failing and stuck.
void OnDataRecv(uint8_t * mac, uint8_t * incomingData, uint8_t len) {
digitalWrite (ledPin, myData.pinStatus); //change pin status
memcpy(&myData, incomingData, sizeof(myData));
Send();
Serial.print("Bytes received: ");
Serial.print (len);
Serial.println ();
Serial.print("Pin Status is : ");
Serial.println(myData.pinStatus); //audit incoming data
Thank you...
it looks perfect
A very welcome relief after many frustrating days.
i should be able to sort any hickups (if any) from here
I really appreciate the assitance and time you have (both ) given.