Hi @agz2007 ,
with using delay() you would have to press the push-button in exact that microsecond
when the final delay()
has finished.
This would be very unconvenient.
I guess you want the code that whenever you press the button regardless of what activity the code is doing sucking/pausing/purging the pump shall be stopped.
And if you press the button again start pump/purge sequence new
To make a program responsive all the time even if the program is "waiting" this requires a different style of coding which is called non-blocking.
This has radical consequences to code in a different way.
-
The code is organised in functions, where each part of the code that builds a senseful sub-unit is put into its own function. These functions shall have a self-explaining name.
-
all functions must be coded in a way to quickly run down in a jump in / jump out manner. This enables to do multiple things fast after each other so it seems all things happen in parallel.
You have to check for button-presses all the time
-
while your pump is sucking in
-
while the code is pausing with pump switched off
-
while your pump is purging out
-
if your code should "wait" some time before going on with a new action this must be done with non-blocking timing
A program with a functionality of be responsive to buttonpresses all the time while executing a sequence of steps suck in / wait / purge out
this program jumps up to a medium advanced level with medium advanced programming-techniques
The sequence of suck in / wait / purge out /
is coded using a state-machine
I estimate your first reaction to the code below will be oh my god what is all this???
Well (just) medium advanced coding.
But this code is well organised in parts where each part does a sensefull thing like
example
void SuckingIn() {
digitalWrite(L293_3A_Pin, HIGH);
digitalWrite(L293_4A_Pin, LOW);
}
set the two IO-pins that are connected to the L293D driver-chip to make the DC-motor run for sucking
all details have self-explaining names
So give yourself 30 minutes time to read in the code scrolling up and down to understand how it works together.
You will still have mutliple questions. Just ask them in postings
The code uses the library toggle.h which "delivers" a functionality that a momentary push-button can be used as a toggle-switch
here is the code
/* 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 () {
myButton.poll();
latchedButtonState = myButton.toggle();
execute_if_Active(latchedButtonState); // 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.h-library demo start/stop executing-code on buttonpress"
const byte switchedON = 1;
const byte switchedOFF = 0;
const byte StartStopButtonPin = 2; // uses a button connected to IO-pin 4
const byte L293_3A_Pin = 3;
const byte L293_4A_Pin = 4;
// take Arduino-Uno / Mega Onboard LED
// as indicator to show status of latched switch
const byte OnBoardLedPin = 13;//LED_BUILTIN;
unsigned long myCounter = 0;
unsigned long myTimer;
// constants with SELF-explaining names for the state-machine
const byte sm_idling = 0;
const byte sm_StartSucking = 1;
const byte sm_Sucking = 2;
const byte sm_PumpPausing = 3;
const byte sm_StartPurging = 4;
const byte sm_Purging = 5;
const byte sm_FinalPausing = 6;
byte PumpState;
// variables of type unsigned long used for non-blocking timing
unsigned long SuckingStarted = 0;
unsigned long PausingStarted = 0;
unsigned long PurgingStarted = 0;
byte latchedButtonState;
#include <Toggle.h> // this line adds the code inside the file Toggle.h that makes it work
Toggle myButton(StartStopButtonPin); // create object that uses IO-pin StartStopButtonPin
void setup() {
Serial.begin(115200); // adjust baudrate in the serial monitor to match the number
Serial.println( F("Setup-Start") );
printFileNameDateTime();
//pinMode (StartStopButtonPin, INPUT);
pinMode(OnBoardLedPin, OUTPUT);
digitalWrite(L293_3A_Pin, LOW); // initially switch OFF
digitalWrite(L293_4A_Pin, LOW); // initially switch OFF
pinMode (L293_3A_Pin, OUTPUT);
pinMode (L293_4A_Pin, OUTPUT);
myButton.begin(StartStopButtonPin); // start the button-object
PumpState = sm_StartSucking;
}
void loop () {
// the function poll() must be called very often
// to read in and update the actual state of the button
myButton.poll();
// the function toggle() returns the state of a SOFWTARE EMULATED latched button
// after a first press of the button the function returns a "1" like I'm in ON-mode
// after the next press of the button the function returns a "0" like I'm in OFF-mode
// after the next press of the button the function returns a "1" like I'm in ON-mode
// etc. etc. etc. etc. etc. ....
// this means that only AFTER releasing the button and then
// pressing DOWN the button NEW
// the value that function toggle() returns is CHANGED
latchedButtonState = myButton.toggle();
execute_if_Active(latchedButtonState); // function that does what its name says
// switch onboard-LED to state of the latched switch to indicate ON/OFF
digitalWrite(OnBoardLedPin, latchedButtonState);
}
// helper-function to visiulise in the serial monitor
// what the code does
void printStateChangeToSerialMonitor() {
static bool lastSwitchState; // static make values persistant
// check if state has CHANGED
if (myButton.toggle() != lastSwitchState) {
// only if state of switch has CHANGED
lastSwitchState = myButton.toggle(); // update variable lastSwitchState
if (myButton.toggle() == switchedON) {
Serial.println();
Serial.println("start executing");
Serial.println();
}
else { // not activated
Serial.println();
Serial.println("stopp executing");
Serial.println();
}
}
}
void execute_if_Active(bool p_IsActivated) {
printStateChangeToSerialMonitor();
if (p_IsActivated) {
// function SuckInPurgeOutSequence() is only executed if parameter p_IsActivated is true
// this parameter "p_IsActivated" is set true or false
// with each press of the button called "StartStopButtonPin"
SuckInPurgeOutSequence();
}
else { // if DE-activated prepare new start of sequence
PumpState = sm_StartSucking;
}
}
void SuckingIn() {
Serial.println(__func__);
digitalWrite(L293_3A_Pin, HIGH);
digitalWrite(L293_4A_Pin, LOW);
}
void PurgeOut() {
Serial.println(__func__);
digitalWrite(L293_3A_Pin, LOW);
digitalWrite(L293_4A_Pin, HIGH);
}
void PumpOFF() {
Serial.println(__func__);
digitalWrite(L293_3A_Pin, LOW);
digitalWrite(L293_4A_Pin, LOW);
}
void SuckInPurgeOutSequence() {
PrintStateChange();
switch (PumpState) {
case sm_idling:
// simply do nothing
break; // immidiately jump down to END-OF-SWITCH
case sm_StartSucking:
SuckingIn();
SuckingStarted = millis(); // store snapshot of time into variable named "SuckingStarted"
PumpState = sm_Sucking;
break; // immidiately jump down to END-OF-SWITCH
case sm_Sucking:
if ( TimePeriodIsOver(SuckingStarted, 3000) ) {
PumpOFF();
PausingStarted = millis(); // store snapshot of time into variable named "PausingStarted"
PumpState = sm_PumpPausing;
}
break; // immidiately jump down to END-OF-SWITCH
case sm_PumpPausing:
if ( TimePeriodIsOver(PausingStarted, 1000) ) {
PumpState = sm_StartPurging;
}
break; // immidiately jump down to END-OF-SWITCH
case sm_StartPurging:
PurgeOut();
PurgingStarted = millis(); // store snapshot of time into variable named "PurgingStarted"
PumpState = sm_Purging;
break; // immidiately jump down to END-OF-SWITCH
case sm_Purging:
if ( TimePeriodIsOver(PurgingStarted, 3000) ) {
PausingStarted = millis(); // store snapshot of time into variable named "PausingStarted"
PumpOFF();
PumpState = sm_FinalPausing;
}
break; // immidiately jump down to END-OF-SWITCH
case sm_FinalPausing:
if ( TimePeriodIsOver(PausingStarted, 1000) ) {
PumpState = sm_StartSucking;
}
break; // immidiately jump down to END-OF-SWITCH
} //END-OF-SWITCH
}
void PrintStateChange() {
static byte lastState;
if (lastState != PumpState) {
Serial.print(" changed from state ");
Serial.print(lastState);
Serial.print(" to new state ");
Serial.print(PumpState);
Serial.print(": ");
lastState = PumpState;
switch (PumpState) {
case sm_idling:
Serial.println("sm_idling");
break; // immidiately jump down to END-OF-SWITCH
case sm_StartSucking:
Serial.println("sm_StartSucking");
break; // immidiately jump down to END-OF-SWITCH
case sm_Sucking:
Serial.println("sm_Sucking");
break; // immidiately jump down to END-OF-SWITCH
case sm_PumpPausing:
Serial.println("sm_PumpPausing");
break; // immidiately jump down to END-OF-SWITCH
case sm_StartPurging:
Serial.println("sm_StartPurging");
break; // immidiately jump down to END-OF-SWITCH
case sm_Purging:
Serial.println("sm_Purging");
break; // immidiately jump down to END-OF-SWITCH
case sm_FinalPausing:
Serial.println("sm_FinalPausing");
break; // immidiately jump down to END-OF-SWITCH
default:
Serial.println("unknown state");
break; // immidiately jump down to END-OF-SWITCH
} // END-OF-SWITCH
}
}
// 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
// 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
}
best regards Stefan