hm seems to become my standard-response on the forum:
Democode that shows an easy to use non-blocking timer-function based on function millis().
@The example-code-mainter-Team at Arduino.cc:
The Blink without delay-example code scatters around the variables.
This makes it difficult to understand and how to adapt it.
The advantage of my function TimePeriodIsOver(myTimerVariable, myPeriod)
is that the code that does the timing is kept together in this function instead of beeing scattered around inbetween other code.
unsigned long DemoTimer = 0; // variables that are used to store values of function millis()
unsigned long DemoTimerTwo = 0; // the must be of type unsigned long to work properly all the time
unsigned long DemoTimerThree = 0;
unsigned long DoDelayTimer = 0;
unsigned long myCounter = 0;
// helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - expireTime >= TimePeriod )
{
expireTime = currentMillis; // set new expireTime
return true; // more time than TimePeriod) has elapsed since last time if-condition was true
}
else return false; // not expired
}
void setup() {
Serial.begin(115200);
Serial.println("Program started activate Show timestamp in serial monitor");
Serial.println("maximise window of serial monitor");
Serial.println("to see the the messages in full.......................................................length");
}
void myDemofunction_1() {
Serial.println("once per second Huhu ! time for Action A ");
Serial.print("myCounter=");
Serial.println(myCounter);
}
void myDemoFuncB() {
Serial.println("once every 3 seconds Hi there time for Action B once every 3 seconds");
}
void my_Demo_function_3() {
Serial.print("once every 5 seconds ready now");
for ( int i = 0; i< 40; i++) {
Serial.print(".");
}
Serial.println("time for Action C once every 5 seconds");
}
void loop() {
myCounter++; // count up very fast to demonstrate the non-blocking character
if ( TimePeriodIsOver(DemoTimer, 1000) ) {
myDemofunction_1();
}
if ( TimePeriodIsOver(DemoTimerTwo, 3000) ) {
myDemoFuncB();
}
if ( TimePeriodIsOver(DemoTimerThree, 5000) ) {
my_Demo_function_3();
}
// show the effect of BLOCKING timing caused by function delay()
if ( TimePeriodIsOver(DoDelayTimer, 20000) ) {
Serial.println("every 20 seconds execute delay(5500)... to make all other timers overdue");
Serial.print("value of myCounter right before delay =");
Serial.println(myCounter);
delay(5500);
Serial.print("value of myCounter right AFTER delay =");
Serial.println(myCounter);
Serial.println("as delay(5500 has BLOCKED code-execution all three timers are overdue");
Serial.println("which means all three timers fire in the SAME microsecond one after the other ");
}
}
/*
the basic principle of non-blocking timing is to check if a defined timeinterval
has passed by.
This can be done by using the function millis()
The function millis()gives back the amount of milliseconds (hence the name millis)
that have passed by since power-up of the microcontroller.
It counts up to 2^32 which means reaching the max-value is reached after 49 days.
There is a calculation-technique that even "rollover" from max to zero is handled
automatically the right way
This non-blocking timing needs a timer-variable which is used for taking
snapshots of time as a comparison-point
The variable-type for this variable MUST be of type unsigend long
to make it work reliably all the time
unsigned long myLcdUpdateTimer;
now the following construction executes the code inside the if-condition
only once every two seconds
if ( TimePeriodIsOver(myLcdUpdateTimer,2000) ) {
// time for timed action
}
additionally the code demonstrates how you can code your own functions
and how to call/execute them.
The names of the functions are a bit lengthy to demonstrate which names
inside the code you can choose freely and to demonstrate where really a relation
between names is and where NO relation between names is
*/
@edichumo The description of the functionality that you want to have sounds like a codingtechnique called "state-machine" will be useful.
Another thing to learn. Once you have learned it you will never go back. Because state-machines reduce the number of conditions and flag-variables subtstantially
// dear newbee,
// this program demonstrates how "state-machines" work
// at the bottom of this file there is an explanation you should read first
//dear expert: this code contains a lot of comments in a style expert don't use comments
// So if you don't like this just skip this program.
// As an expert you know everything explained here anyway
const byte buttonPin = 12; // GPIO with a Button connected to GND
const byte onBoardLedPin = 13; // GPIO connected to a LED - will be switched on/off
// an enumeration class for the different states of the state machine
enum class States { // Assigns names to a set of numbers starting at 0.
waitForButtonPressed,
switchOnLed,
waitUntilLedOnPeriodeIsOver,
switchOffLed,
waitUnitLedOffPeriodeIsOver
} currentState; // these constants are used for this variable named "currentState"
// variables for timePeriodes
const unsigned long ledOnPeriode = 6000; // how long should the LED be on (in milliseconds), make const if variable never changes
const unsigned long ledOffPeriode = 10000; // how long should the LED be Off (in milliseconds), make const if variable never changes
const byte pressed = LOW; // The button connects to GND, so a pressed button will be LOW
long countLoops = 0; // used to count loops, Demo only, not needed in your final sketch
// If you are interested in the main-code scroll down to
// void setup()
/*
the finite state machine
*/
void handleFiniteStateMachine() {
unsigned long currentMillis = millis();
static unsigned long previousMillis = 0; // timestamp when the current State was started, as these variables must survive after the end of the function, they are declared as static
// this is the state-machine based on the switch-case-statement
// depending on the value of variable "currentState" only the commands
// below one "case" gets executed
switch (currentState) {
case States::waitForButtonPressed:
// a state that waits for an external input to occur
if (digitalRead(buttonPin) == pressed) {
Serial.println( F("button is pressed") );
currentState = States::switchOnLed;
}
break;
case States::switchOnLed:
// a state that does an action just ONE time
// because the state-variable is set immediately to the next state
Serial.println( F("I switch on the LED start non-blocking-timing..") );
digitalWrite(onBoardLedPin, HIGH);
previousMillis = currentMillis;
currentState = States::waitUntilLedOnPeriodeIsOver;
break;
case States::waitUntilLedOnPeriodeIsOver:
// a state that waits for a timePeriod to pass by
if (currentMillis - previousMillis >= ledOnPeriode) {
Serial.println( F("Wait-Time with LED ON is over)") );
currentState = States::switchOffLed;
}
break;
case States::switchOffLed:
// a state that does an action just ONE time
// because the state-variable is set immetiately to the next state
Serial.println( F("I switch OFF LED. Start non-blocking timing...") );
digitalWrite(onBoardLedPin, LOW);
previousMillis = currentMillis;
currentState = States::waitUnitLedOffPeriodeIsOver;
break;
case States::waitUnitLedOffPeriodeIsOver:
// a state that waits for a timePeriod to pass by
if (currentMillis - previousMillis >= ledOffPeriode) {
Serial.println( F("Wait-Time with LED OFF is over") );
Serial.println( F("I'm ready for a new buttonpress") );
currentState = States::waitForButtonPressed;
}
break;
}
}
// below here are "helper-functions" for the visualisation
// of what the program-FLOW does
/*
this is just a helper function to check if it is time to trigger serial output
*/
void handleTimerForSerial () {
unsigned long currentMillis = millis();
static unsigned long previousMillis; // keeps timestamp of last print
if ( currentMillis - previousMillis >= 1000 )
{
previousMillis = currentMillis; // set new expireTime
printStateInfo();
}
}
/*
helper function
to print the actual state of the finite state machine
Demo only, not needed in your final sketch
*/
void printActualStateText(States p_state) {
// as the state-machine can only have ONE state at a time
// here the switch-case-statement can be used too
switch (p_state) {
case States::waitForButtonPressed:
Serial.print( F(" Wait for Button-Press") );
break;
case States::switchOnLed:
Serial.print( F(" Switch ON LED ") );
break;
case States::waitUntilLedOnPeriodeIsOver:
Serial.println( F(" Wait until LED-ON-Time has passed by \n"
"Keep LED On some time. Time passed by since LED switched on ") );
//Serial.print(millis() - ledOnPeriodeStart);
break;
case States::switchOffLed:
Serial.print( F(" Switch OFF LED ") );
break;
case States::waitUnitLedOffPeriodeIsOver:
Serial.println( F(" Wait until some time is over\n"
"Wait some time. Time passed by since LED switched OFF ") );
//Serial.print(millis() - ledOffPeriodeStart);
break;
// and it offers the opportunity of saying something if the value is not defined
default:
Serial.print( F("unknown state parameter p_state has value ") );
Serial.println((int)p_state); // cast to a printable integer
break;
}
}
/*
helper function
to print the current state of the state machine
Demo only, not needed in your final sketch
*/
void printStateInfo() {
Serial.println();
Serial.print( F("CountLoops ") );
Serial.print(countLoops);
Serial.print( F(" I'm in state ") );
Serial.print((int)currentState); // cast the current state to a printable integer
printActualStateText(currentState);
Serial.println();
}
/*
helper function
to be able to determine which exactly sourcecode-version is running
I add this function to all my programs
the content is updated EVERY time you compile new (at least the time-stamp)
*/
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__) );
}
void setup() {
Serial.begin(115200);
Serial.println( F("Setup-Start") );
printFileNameDateTime();
pinMode(buttonPin, INPUT_PULLUP);
digitalWrite(onBoardLedPin, LOW);
pinMode(onBoardLedPin, OUTPUT);
currentState = States::waitForButtonPressed;
Serial.println( F("press button at least for two seconds to start the demo") );
}
// parts of the program that belong to one functional unit are coded in their
// own function where each function has a self-explaining name
// This makes it much easier to test code and keep an overview
// with the growing of the code
void loop() {
handleTimerForSerial(); // triggers the timer for a periodically print to serial
handleFiniteStateMachine(); // the finite state machine is called thousands of times per second but still does all the timing
countLoops++; // counts up very fast to show how fast the loop is running, Demo only, not needed in your final sketch
}
// explanation:
// dear newbee,
// the program expects a button connected to IO-pin 12
// and does switch on / off IO-pin 13 which is connected to the onboard-LED
// on an arduino uno-board
// a state-machine is a medium advanced programming-technique.
// So if you hardly know what a constant, a variable, an if-condition is
// understanding this program can be ambitious and hard
// in this case I recommend learning the fundamental basics first
// I'm very interested in your experience if this program is easy to understand
// or hard to understand
// so whatever questions you have about this code your questions from YOU are a
// very welcomed feedback to improve the understandability
// the main code has around 100 lines. So indeed it will take some time to understand it.
// for watching what the program-FLOW does start the serial monitor and adjust baud to
// 115200 baud
// after 20 seconds deactivate "autoscroll" in the serial monitor or unplug your microcontroller-board
// and then scroll back the serial monitor and start reading the lines
// to understand what the program does.
// again: If you have any questions feel free to ask the questions in the Arduino-Forum
// I'm very interested in your questions because your questions are feedback what is still
// difficult to understand.
another state-mamchine example is here
best regards Stefan