Hello everyone, please I am trying to figure out a program that counts down time (delays) before executing a command (like turning on or off an LED) after a condition (read a potentiometer) or set of conditions have been met, but I can't seem to get it right. I have a feeling millis() function is going to work out but I don't know how to put my codes properly. Please can anyone help me out with it
Show us a good schematic of your proposed circuit.
Show us a good image of your ‘actual’ wiring.
Give links to components.
In the Arduino IDE, use Ctrl T or CMD T to format your code then copy the complete sketch.
Use the </> icon from the ‘reply menu’ to attach the copied sketch.
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
int ledPin = 3;
int potPin = A3;
int potVal;
const unsigned long event1 = 2000;
unsigned long prevT = 0;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(potPin, INPUT);
digitalWrite(ledPin, HIGH);
Serial.begin(9600);
}
void loop() {
potVal = analogRead(potPin);
unsigned long currentT = millis();
if (potVal > 300 && potVal <= 500) {
if (currentT >= event1) {
digitalWrite(ledPin, HIGH);
prevT = currentT;
}
}
else {
digitalWrite(ledPin, LOW);
}
Serial.print("potVal: ");
Serial.print(potVal);
Serial.print(" ");
Serial.println(millis());
}
thank you for your reply, but I dont want it to be repititive, I actually want to a countdown timer for my TV set such that when i turn a potentiometer knob to a certain position it starts counting down to a preset time and a relay disconnects power supply to the TV set.
i want to use the potentiometer as the rotary switch selector that would select preset times like 30mins, 1hour, 2hours...e.t.c and when the time elapses the TV is turned off.
use a flag variable:
somehow your microcontroller must be informed that the countdown-time has started
This coud be done by a buttonpress
the buttonpress sets a flagvariable "countDownStarted" to true and stores a snapshot of time
startOfCountDown = millis()
your non-blocking timing does check
if (countDownStarted) {
if(Timing-condition)
switch off
countDownStarted = false;
as variable countDownStarted is set to false the first if-condition is false and the timing-condition is not executed until a buttonpress "start countdown" is done new
best regards Stefan
Hello edichumo
For your project an event controlled time-handler is needed.
As base you can use the BlinkWithOutDelay IDE example as mother of all timers used within all Arduino sketches.
Do you have any experiences in coding in C++?
Have a nice day and enjoy coding in C++.
happy C++ coding to me means: use functions
/* Blink without delay
improved to show how to use functions 2022
by StefanL38
This example code is in the public domain.
*/
// constants won't change.
const byte ledPin = LED_BUILTIN; // the number of the LED pin
const unsigned long interval = 1000; // interval at which to blink (milliseconds)
// Variables will change:
unsigned long myTestTimer = 0; // variables used for timing MUST be of type unsigned long
byte ledState = LOW; // ledState used to set the LED
// the timing-function
// returns TRUE or false depending on how much time has passed by
boolean TimePeriodIsOver (unsigned long &periodStartTime, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - periodStartTime >= TimePeriod )
{
periodStartTime = 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 toggleLED() {
// if the LED is off turn it on and vice-versa:
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
void setup() {
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
}
void loop() {
// here is where you'd put code that needs to be executed
// with each iteration of loop
// check to see if it's time to blink the LED; that is, if the difference
// between the current time and last time you blinked the LED is bigger than
// the interval at which you want to blink the LED.
if ( TimePeriodIsOver(myTestTimer, interval) ) { // check how much time has passed by
// ===>> call timed FUNCTION here <=========
toggleLED();
}
}
best regards Stefan
Well I have no experience in C++ coding but I have been watching Paul McWhorter's Arduino tutorial on YouTube for some time
Hello Stefan,
thanks for your reply. I noticed the code you posted here is for blinking and LED without using the delay() function. But what I actually want is for the LED to turn ON/HIGH when I press a button or turn a potentiometer and stay ON/HIGH for a period of time and after that set time has elapsed it turns back OFF/LOW similar to the way an alarm clock would work
what I actually want is for a relay contacts to close when I press a button or turn a potentiometer and stay closed for a period of time and after that set time has elapsed the relay contacts open similar to the way an alarm clock would work.
I hope this description is explanatory enough.
ALGORITHMIC Example:
- press a button or turn a potentiometer.
- a relay contacts close.
- after desired time (say 2 hours) elapses relay contacts open.
The BWD portion of the sketch offered is directly applicable to your timing requirement.
Try to use the same technique, if you run into something that needs explanation ask for help.
Of course show us what you tried.
alright let me try it out, thanks
/* Blink without delay
improved to show how to use functions 2022
by StefanL38
This example code is in the public domain.
*/
// constants won't change.
const byte ledPin = 3; // the number of the LED pin
int potPin = A3; // the pin for potentiometer probe
const unsigned long interval = 1000; // interval at which to blink (milliseconds)
// Variables will change:
unsigned long myTestTimer = 0; // variables used for timing MUST be of type unsigned long
byte ledState = LOW; // ledState used to set the LED
int potVal; // analog value from potentiometer
// the timing-function
// returns TRUE or false depending on how much time has passed by
boolean TimePeriodIsOver (unsigned long &periodStartTime, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - periodStartTime >= TimePeriod )
{
periodStartTime = 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 toggleLED() {
// if the LED is off turn it on and vice-versa:
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
void setup() {
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
pinMode(potPin, INPUT);
Serial.begin(9600);
}
void loop() {
// here is where you'd put code that needs to be executed
// with each iteration of loop
potVal = analogRead(potPin);
if (potVal > 300 && potVal <= 500) {
// check to see if it's time to blink the LED; that is, if the difference
// between the current time and last time you blinked the LED is bigger than
// the interval at which you want to blink the LED.
if ( TimePeriodIsOver(myTestTimer, interval) ) { // check how much time has passed by
// ===>> call timed FUNCTION here <=========
toggleLED();
}
}
Serial.print("potVal: "); //prints the description of string
Serial.println(potVal); // prints analog value of potentiometer to serial monitor
}
after putting in my potentiometer code i still got the same repetitive blinking. I don`t want the LED to blink, I want it to stay ON until time elapses and then go OFF. it MUST NOT come ON a second time. just once , like an alarm clock
What do you want to happen if the 2 hour period goes by and the pot is still in the trigger range ?
This might be close:
//
// https://forum.arduino.cc/t/countdown-timer-with-potentiometer/945145/17?u=larryd
//
// Version YY/MM/DD Comments
// ======= ======== ===============================================
// 1.00 22/01/09 Running code
//
//
#define LEDon HIGH
#define LEDoff LOW
#define ENABLED true
#define DISABLED false
#define OPEN HIGH
#define CLOSED LOW
bool timingFlag = DISABLED;
const byte mySwitch = 2; //5V---Internal Pullup---pin---[switch]---GND
const byte ledPin = 3;
const byte heartbeatLED = 13;
const byte potPin = A3;
int potVal;
//timing stuff
//const unsigned long Interval = 2 * 60 * 60 * 1000ul; //2 hours
//testing
const unsigned long Interval = 5 * 1000; //5 seconds
unsigned long periodStartTime;
unsigned long heartbeatMillis;
//*********************************************************************
void setup()
{
Serial.begin(9600);
pinMode(heartbeatLED, OUTPUT);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LEDoff);
pinMode(mySwitch, INPUT_PULLUP);
} //END of setup()
//*********************************************************************
void loop()
{
//********************************
//time to toggle the heartbeat LED ?
if(millis() - heartbeatMillis >= 500)
{
//restart the TIMER
heartbeatMillis = millis();
//toggle the LED
digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
}
//********************************
//once the pot is in the trigger range, the user will need to take it <----<<< N O T E
//out of range, otherwise the 2 hour period will restart once the TIMER expires
potVal = analogRead(potPin);
//if we are not timing, is the pot in range ?
if (timingFlag == DISABLED && potVal > 300 && potVal <= 500)
{
digitalWrite(ledPin, LEDon);
//Start the TIMER
periodStartTime = millis();
//ENABLE the TIMER
timingFlag = ENABLED;
}
//********************************
//if we are not timing, did the switch get closed ?
if (timingFlag == DISABLED && digitalRead(mySwitch) == CLOSED)
{
digitalWrite(ledPin, LEDon);
//Start the TIMER
periodStartTime = millis();
//ENABLE the TIMER
timingFlag = ENABLED;
}
//********************************
//if enabled, has the this TIMER expired ?
if (timingFlag == ENABLED && millis() - periodStartTime >= Interval)
{
//disable this TIMER
timingFlag = DISABLED;
digitalWrite(ledPin, LEDoff);
}
//********************************
//Other non blocking code goes here
//********************************
} //END of loop()
//*********************************************************************
Have you read what I posted in post #7 Countdown timer with potentiometer - #7 by StefanL38
On an alarm-clock you have a state-change from
- alarm-time not (yet) reached
to - alarm-time reached
what is your state-change that makes your code know:
the count-down time starts here
= switch on LED and from now on
and in 30 min. 1h, 2h in the future switch LED off
You could use the potentiometers value that changes from beeing outside the range
(potVal > 300 && potVal <= 500)
to be inside this range. But this would mean:
Your code is running very very fast as soon as you enter the range 300-500 this would be taken as the trigger.
You would not be able to choose a value
So there must be something additional that is used as the trigger to say
the count-down time starts here
My suggestion: a button press.
best regards Stefan
I would want the relay to be deactivated after the elapsed time
Okay does it mean I have to add a pushbutton to the set up? Or do I need to scrap the potentiometer and make use of only push buttons?