make a short non-blocking "action" through a state-CHANGE
// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *
// Take it for granted at the moment scroll down to void setup
// start of macros dbg and dbgi
#define dbg(myFixedText, variableName) \
Serial.print( F(#myFixedText " " #variableName"=") ); \
Serial.println(variableName);
// usage: dbg("1:my fixed text",myVariable);
// myVariable can be any variable or expression that is defined in scope
#define dbgi(myFixedText, variableName,timeInterval) \
do { \
static unsigned long intervalStartTime; \
if ( millis() - intervalStartTime >= timeInterval ){ \
intervalStartTime = millis(); \
Serial.print( F(#myFixedText " " #variableName"=") ); \
Serial.println(variableName); \
} \
} while (false);
// usage: dbgi("2:my fixed text",myVariable,1000);
// myVariable can be any variable or expression that is defined in scope
// third parameter is the time in milliseconds that must pass by until the next time a
// Serial.print is executed
// end of macros dbg and dbgi
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *
const byte SIGNAL_Pin = 4;
void PrintFileNameDateTime() {
Serial.println( F("Code running comes from file ") );
Serial.println( F(__FILE__) );
Serial.print( F(" compiled ") );
Serial.print( F(__DATE__) );
Serial.print( F(" ") );
Serial.println( F(__TIME__) );
}
// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - startOfPeriod >= TimePeriod ) {
// more time than TimePeriod has elapsed since last time if-condition was true
startOfPeriod = currentMillis; // a new period starts right here so set new starttime
return true;
}
else return false; // actual TimePeriod is NOT yet over
}
unsigned long MyTestTimer = 0; // Timer-variables MUST be of type unsigned long
const byte OnBoard_LED = 13;
byte actualSignalState;
byte lastSignalState;
// to make your code easy to understand
// rename this variable that the new name describes
// SPOT-ON what the PURPOSE of the variable is
boolean ActionShallBeActive = false; // software-switch for switching on/off pulse-creation
// to make your code easy to understand
// rename this variable that the new name describes
// SPOT-ON what the PURPOSE of the variable is
unsigned long actionTimer = 0; // variable used for non-blockingtiming MUST be of type unsigned long
void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
static unsigned long MyBlinkTimer;
pinMode(IO_Pin, OUTPUT);
if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
}
}
// to make your code easy to understand
// rename this function that the new name describes
// SPOT-ON what the code does
void actionOn_low_HIGH_change() {
dbg("actionOn_low_HIGH_change",111);
// add your code here
}
// to make your code easy to understand
// rename this function that the new name describes
// SPOT-ON what the code does
void actionOn_HIGH_low_change() {
dbg("actionOn_HIGH_low_change()",222);
// add your code here
}
// to make your code easy to understand
// rename this function that the new name describes
// SPOT-ON what the code does
void actionFinalisingAfterChange() {
dbg("actionFinalisingAfterChange()",999);
// add your code here
}
void setup() {
Serial.begin(115200);
Serial.println( F("/n Setup-Start /n") );
PrintFileNameDateTime();
pinMode(SIGNAL_Pin, INPUT);
// eventually on startup execute finalising code
// example switch OFF IO-pins
//actionFinalisingAfterChange()
// initialise actualSignalState and lastSignalState to be equal on power-on
actualSignalState = digitalRead(SIGNAL_Pin);
lastSignalState = actualSignalState;
}
void loop() {
BlinkHeartBeatLED(OnBoard_LED, 250);
actualSignalState = digitalRead(SIGNAL_Pin);
//dbgi("0:",actualSignalState,1000);
if (actualSignalState != lastSignalState) { // check if a state-CHANGE has occurred
// if a state-CHANGE has REALLY occured
dbg("state-CHANGE detected",actualSignalState);
// update lastSignalState because we HAVE detected the state-CHANGE
lastSignalState = actualSignalState;
ActionShallBeActive = true; // set flag-variable to true
// store actual time in Timer-variable as initialisation
actionTimer = millis();
if (actualSignalState == HIGH) {
actionOn_low_HIGH_change();
}
if (actualSignalState == LOW) {
actionOn_HIGH_low_change();
}
} // END OF if (actualSignalState != lastSignalState)
if (ActionShallBeActive) {
if ( TimePeriodIsOver(actionTimer,1000) ) { // check if more than 1000 milliseconds of time have passed by
// if 1000 milliseconds of time HAVE passed by
dbg("actiontime is over",actualSignalState);
ActionShallBeActive = false; // RE-set flag-variable to stop action
actionFinalisingAfterChange();
}
}
}
in most projects multiple functions are needed.
And very often it is required that the code is always fast responsive to
incoming data
button-presses
state-changes of switches like limit-switches
etc.
This requires to write code in a consequent NON-blocking way
consequence:
NOWHERE use delay()
NOWHERE use while-loops or do-while-loops
because delay and while-loops are blocking
But how shall repetetive actions be coded then ?
how shall delayed actions be coded then ?
The most important thing is let do
all looping by void loop()
in combination with non-blocking functions that get repeatedly called through the - guess what
looping by void loop()
taking action only from time to time is done by non-blocking timing based on millis()
this means: let do void loop() looping fast all the time and repeatedly check how much time has passed by through comparing a snapshot of time with actual time
work through a sequence of steps is done by state-machines
this means: let do void loop() looping fast all the time and repeatedly call a function that "works through" a sequence of multiple steps".
If changing to a new step shall be done after a certain time it is done by non-blocking timing like described above