/* 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
}
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 whiteledPin = 8;
const byte ToggleButtonPin = 4;
boolean activationMode;
boolean last_activationMode;
void setup() {
Serial.begin(115200); // adjust baudrate in the serial monitor to match the number
Serial.println( F("Setup-Start") );
printFileNameDateTime();
pinMode (whiteledPin, OUTPUT); // used for indicating logging active or not
digitalWrite(whiteledPin, 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 () {
activationMode = GetToggleSwitchState(); // must be executed all the time
if (activationMode != last_activationMode) {
digitalWrite(whiteledPin,activationMode);
last_activationMode = activationMode;
}
}
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;
static 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 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) );
}
// 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
}