Hi everybody,
from time to time I enjoy writing demo-codes for a certain functionality.
EDIT 04.06.2022
user @dlloyd has written a library with toggle-functionality
This means all details how to debounce the button and detecting state-change etc. are hided away from the user inside his library. This makes it easier to apply this functionality but harder to understand.
So in post #3 there is a version that makes use of the toogle.h-library.
The toogle.h-library can be installed from the library-manager inside the Arduino-IDE
In post #4 is a most stripped down version of this demo-code. Both versions have their advantages and disadvantages
This thread contains a demo-code which uses a momentary push-button as a toggle-switch
push activated
push again DE-activated
push again activated
push again DE-activated
etc. etc.
pushing the push-button toggles activation / de-activation of the execution of the function called "myAction()"
This means you simply replace the code inside function "myAction()" with your code that shall only be executed if activated through the push-button
stopping execution of function "myAction()" is done by the next push of the same push-button
The code is well organised in functions. Where each function is a SUB-program doing a senseful unit of things. Splitting the code in this organised way makes function loop super-short
void loop () {
activationMode = GetToggleSwitchState(); // must be executed all the time
execute_if_Active(activationMode); // function that does what its name says
}
The function GetToggleSwitchState() must be executed very often to keep the reaction to the buttonpresses responsive.
Function execute_if_Active() does what its name says
This code is dedicated to newcomers. I wrote it with the intention to be easy to understand. Though if you read this after just three hours of learning programming from zero it is yet challenging.
I'm interested in improving the easyness of understanding even more.
So if you have any questions just post them.
/* explanation of the most important details:
realising a functionality where using a momentary push-button
acts as a toogle-switch
push => activated push again => DE-activated
push again => activated push again => DE-activated
etc. etc. ...
This needs quite some code. This code is well organised in MULTIPLE functions
where each function is a senseful SUB-program
This is the reason why function loop looks super-short:
void loop () {
activationMode = GetToggleSwitchState(); // must be executed all the time
execute_if_Active(activationMode); // function that does what its name says
}
Huh that's all? No. Of course there is more (below function loop)
If you just want to APPLY the functionality to your own code
replace the code inside function void my_Action()
with your own code that shall only be executed if you activated execution
with the momentary push-button
I recommend to RENAME the function my_Action() to a SELF-explaining name
This will cause a compiler-error which shows you where you have to rename
the function-call of this function too (this is inside function execute_if_Active
If you want to understand how it works read and analyse the rest of the functions.
*/
#define ProjectName "Toggle Button demonstration"
// define IO-states for inputs with pull-up-resistors
// pull-up-resistors invert the logig
#define unPressed HIGH
#define pressed LOW
const byte ToggleButtonPin = A0;
bool activationMode = false;
unsigned long myCounter = 0;
unsigned long Print_Timer;
void setup() {
Serial.begin(115200); // adjust baudrate in the serial monitor to match the number
Serial.println( F("Setup-Start") );
printFileNameDateTime();
pinMode (LED_BUILTIN, OUTPUT); // used for indicating logging active or not
digitalWrite(LED_BUILTIN, LOW);
// wire button between IO-pin and GND
// Pull-up-resistor inverts the logic
// unpressed: IO-pin detects HIGH
// pressed: IO-Pin detects LOW
pinMode(ToggleButtonPin, INPUT_PULLUP);
}
void loop () {
// function GetToggleSwitchState() must be executed repeatedly all the time
// to be able to detect if the momentary button is pressed
activationMode = GetToggleSwitchState();
execute_if_Active(activationMode); // function that does what its name says
}
bool GetToggleSwitchState() {
// "static" makes variables persistant over function calls
static bool toggleState = false;
static bool lastToggleState = false;
static byte buttonStateOld = unPressed;
static unsigned long buttonScanStarted = 0;
unsigned long buttonDebounceTime = 50;
unsigned long buttonDebounceTimer;
byte buttonStateNew;
if ( TimePeriodIsOver(buttonDebounceTimer, buttonDebounceTime) ) {
// if more time than buttonDebounceTime has passed by
// this means let pass by some time until
// bouncing of the button is over
buttonStateNew = digitalRead(ToggleButtonPin);
if (buttonStateNew != buttonStateOld) {
// if button-state has changed
buttonStateOld = buttonStateNew;
if (buttonStateNew == unPressed) {
// if button is released
toggleState = !toggleState; // toggle state-variable
} // the attention-mark is the NOT operator
} // which simply inverts the boolean state
} // !true = false NOT true is false
// !false = true NOT false is true
return toggleState;
}
void execute_if_Active(bool p_IsActivated) {
if (p_IsActivated) {
// insert code here that shall only be executed if activated
my_Action();
}
// just for demonstration purposes
printStateChangeToSerialMonitor(p_IsActivated);
}
// helper-function ignore at first
void printFileNameDateTime() {
Serial.print( F("File : ") );
Serial.println( F(__FILE__) );
Serial.print( F("Date : ") );
Serial.println( F(__DATE__) );
Serial.print( F("Project: ") );
Serial.println( F(ProjectName) );
}
// ignore at first
// helper-function for easy to use 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
}
// this is the function that is called
// inside function execute_if_Active()
void my_Action() {
// as a dummy-Action print to the serial monitor but only once per second
if ( TimePeriodIsOver(Print_Timer, 1000) ) {
// if 1000 milliseconds have passed by
myCounter++;
Serial.println(myCounter);
Serial.println("this function is a template");
Serial.println("that demonstrates where to place");
Serial.println("your code that shall only be executed");
Serial.println("if execution is activated by the");
Serial.println("push-button-toggle-switch functionality");
Serial.println();
Serial.println();
}
}
// function used ONLY for demonstration purposes
void printStateChangeToSerialMonitor(boolean activationState) {
static bool lastactivationState; // flag-variable to remember the state at last call
if (activationState != lastactivationState) {
// only if state of parameter activationState has CHANGED
if (activationState == true) {
Serial.println();
Serial.println("start executing");
Serial.println();
digitalWrite(LED_BUILTIN, HIGH);
}
else { // not activated
Serial.println();
Serial.println("stopp executing");
Serial.println();
digitalWrite(LED_BUILTIN, LOW);
}
lastactivationState = activationState; // update variable lastIsActivated
}
}
Be the change you want to see in the world
best regards Stefan