Hi everybody,
recently I wrote some testcode to test wireless communication with the ESP-NOW-protocol for ESP8266/ESP32 boards. The testcode used delay() in the mainloop. The datatransmission was very unreliable. I found out it was caused by the command delay(). Looking back it is clear sure a delay delays = blocking the whole processor from doing anything else than delaying. Though delay() is the second command a newbee learns through the blink-the-LED-example. My Opioning throw out the command delay. It can always be replaced with something non-blocking.
So I wrote a tiny library that has some functions to use non-blocking timers and to encapsulate some of the details that were used. My Opinion is this "library" is a little bit too small and not well tested enough to open up another git-repositery. Additional I'm not very familiar with how to use github.
So I decided to post my code here:
here is the header-file
#ifndef nonBlocking_Timer_H
#define nonBlocking_Timer_H
#include <Arduino.h>
class nonBlocking_Timer
{
private:
byte dummy;
static const uint8_t nBTc_MaxNumOfTimers = 20;
uint8_t nBTv_NoOfRegisteredTimers;
uint8_t nBTv_TimerIDNr[nBTc_MaxNumOfTimers + 1];
uint32_t nBTv_TimeStampMillis[nBTc_MaxNumOfTimers + 1];
public:
nonBlocking_Timer(byte dummy);
void init();
int OccupyTimer();
int StartTimer(int8_t p_TimerID_Nr);
int UpDateTimer(int8_t p_TimerID_Nr, uint32_t p_IntervalMillis);
uint32_t elapsedTime(int8_t p_TimerID_Nr);
};
#endif
here the *.CPP-file
#include <nonBlocking_Timer_class.h>
nonBlocking_Timer::nonBlocking_Timer(byte dummy)
{ this->dummy = dummy;
init();
}
void nonBlocking_Timer::init()
{ dummy = dummy; }
int nonBlocking_Timer::OccupyTimer()
{
int l_Result = -1; // initialise value to return with value meaning "All timers in use"
// if a free timer is available
if (nBTv_NoOfRegisteredTimers < nBTc_MaxNumOfTimers)
{nBTv_NoOfRegisteredTimers = nBTv_NoOfRegisteredTimers + 1;
l_Result = nBTv_NoOfRegisteredTimers;} // return TimerID-Number
else
{l_Result = -1;} // return -1 for indicating "All timers in use"
return l_Result;
}
int nonBlocking_Timer::StartTimer(int8_t p_TimerID_Nr)
{ int l_result = -1; // initialise result with -1 indicating something went wrong
// if TimerID_Nr is in the allowed range
if ( (p_TimerID_Nr >0) && (p_TimerID_Nr <= nBTc_MaxNumOfTimers) )
{nBTv_TimeStampMillis[p_TimerID_Nr] = millis();
l_result = 0;} //
else
{l_result = -1;}
return l_result;
}
int nonBlocking_Timer::UpDateTimer(int8_t p_TimerID_Nr, uint32_t p_IntervalMillis)
{
int l_result = -1;
// updating the timer means add amount of milliseconds to timestamp given with p_IntervalMillis
// this means the milliseconds passed while other code was executeddoes NOT cause an extra-delay
// the difference to TFm_StartTimer is that TFm_StartTimer stores the current millis()
// if you call TFm_elapsedTime the REAL interval is the value of TFm_elapsedTime PLUS the milliseconds passed through exexution of additional code
// Timer-ID-Nr is in the allowed range
if ( (p_TimerID_Nr > 0) && (p_TimerID_Nr <= nBTc_MaxNumOfTimers) )
{nBTv_TimeStampMillis[p_TimerID_Nr] = nBTv_TimeStampMillis[p_TimerID_Nr] + p_IntervalMillis;
l_result = 0;}
return l_result;
}
uint32_t nonBlocking_Timer::elapsedTime(int8_t p_TimerID_Nr)
{
uint32_t l_result = -1; // initialise return-value with "something went wrong"
// check if Timer-ID-Nr is in the allowed range
if ( (p_TimerID_Nr > 0 ) && (p_TimerID_Nr <= nBTc_MaxNumOfTimers) )
{ l_result = nBTv_TimeStampMillis[p_TimerID_Nr] - millis();}
return l_result;
}
and here some testcode that demonstrates how to use it
#include <nonBlocking_Timer_class.h>
const byte Dummy = 1;
nonBlocking_Timer nbTimer_object(Dummy); // create object of class
int DemoTimerA_IDNr; // variables that are used to access the right variables inside
int DemoTimerB_IDNr; // the nonBlocking_Timer-object through their assigend ID-Nr.
int DemoTimerC_IDNr;
void setup()
{
Serial.begin(115200);
Serial.println("Program started");
// occupy and start timers
DemoTimerA_IDNr = nbTimer_object.OccupyTimer();
if (DemoTimerA_IDNr > 0) // Check if assigning a Timer-IDNr was successful
{nbTimer_object.StartTimer(DemoTimerA_IDNr);}
else
{Serial.println("registering Timer A was NOT successful!");}
DemoTimerB_IDNr = nbTimer_object.OccupyTimer();
if (DemoTimerB_IDNr > 0) // Check if assigning a Timer-IDNr was successful
{nbTimer_object.StartTimer(DemoTimerB_IDNr);}
else
{Serial.println("registering Timer B was NOT successful!");}
DemoTimerC_IDNr = nbTimer_object.OccupyTimer();
if (DemoTimerC_IDNr > 0) // Check if assigning a Timer-IDNr was successful
{nbTimer_object.StartTimer(DemoTimerC_IDNr);}
else
{Serial.println("registering Timer C was NOT successful!");}
}
void loop()
{
if (nbTimer_object.elapsedTime(DemoTimerA_IDNr) > 1000)
{Serial.println(" TimerA overDue");
nbTimer_object.UpDateTimer(DemoTimerA_IDNr,1000);
}
if (nbTimer_object.elapsedTime(DemoTimerB_IDNr) > 2000)
{Serial.println(" TimerB overDue");
nbTimer_object.UpDateTimer(DemoTimerB_IDNr, 2000);
}
if (nbTimer_object.elapsedTime(DemoTimerC_IDNr) > 3000)
{Serial.println(" TimerC overDue");
nbTimer_object.UpDateTimer(DemoTimerC_IDNr, 3000);
}
}
I tested this on a ESP8266 Wemos D1 mini board. The code has nothing ESP8266-specific so it should run on any board that can be chosen inside the Arduino-IDE.
For making use of the include-files you have to create a folder with the exact same name as the *.h and the *.cpp-file
Foldername:
nonBlocking_Timer_class
This folder must be s subfolder of the "libraries"-folder.
If you let search windows for folders named libraries you will find multiple ones
The exact location of the right libraries-folder can differ depending on the location where you have installed the Arduino-IDE. For me it is working in this folder
C:\Users\Stefan\Documents\Arduino\libraries
So the nonBlocking_Timer_class-folder is located here
C:\Users\Stefan\Documents\Arduino\libraries\nonBlocking_Timer_class
hope this information will help newbees to get the library installed at the right place.
especially at newbees: I have written some explanations inside the code and the demo shows the use of multiple timers with different intervals. If you have any questions post them. I want to expand the explanations based on the newbee-questions. So feel free to ask whatever you like.
best regards
Stefan
nonBlocking-Timer-Demo-code.ino (1.76 KB)
nonBlocking_Timer_class.zip (1.61 KB)