Detect when the input becomes HIGH (not when it is HIGH)
save the value of milis() ay that time as the start time
set a boolean to true to flag the fact that timing is taking place
later in loop() if the boolean is true and the current millis() - start time is greater than the required period then the period is over set the boolean to false to stop timing and do whatever you want
do whatever else you need in loop() but don't use any blocking code
#define VALVE_PIN 4
#define VALVE_ON_TIME 150
#define VALVE_OFF_TIME 400
void setup() {
Serial.begin(115200);
pinMode(VALVE_PIN, OUTPUT);
}
void loop() {
static uint32_t timeCapture;
static bool valveOn;
if ((millis() - timeCapture) >= (valveOn ? VALVE_ON_TIME : VALVE_OFF_TIME)) {
timeCapture = millis();
digitalWrite(VALVE_PIN, valveOn = !valveOn);
// The next line is for debug/workbenching only! Remove before using. Casting to 'String' is a bad idea.
Serial.println((String)"Time(ms): " + millis() + " " + (valveOn ? "Valve On" : "Valve Off"));
}
}
Output:
Time(ms): 400 Valve On
Time(ms): 550 Valve Off
Time(ms): 950 Valve On
Time(ms): 1100 Valve Off
Time(ms): 1500 Valve On
Time(ms): 1650 Valve Off
Time(ms): 2050 Valve On
Time(ms): 2200 Valve Off
Time(ms): 2600 Valve On
Time(ms): 2750 Valve Off
Hello viski3118
See below the answer.
This example sketch uses class-less OOP by meaning to have objects for timing, button and led and a method to perform the desired function.
/* BLOCK COMMENT
ATTENTION: This Sketch contains elements of C++.
https://www.learncpp.com/cpp-tutorial/
Many thanks to LarryD
https://europe1.discourse-cdn.com/arduino/original/4X/7/e/0/7e0ee1e51f1df32e30893550c85f0dd33244fb0e.jpeg
https://forum.arduino.cc/t/toggling-a-switch-without-delay/960308
*/
#define ProjectName "Toggling a switch without delay"
// HARDWARE AND TIMER SETTINGS
// YOU MAY NEED TO CHANGE THESE CONSTANTS TO YOUR HARDWARE AND NEEDS
constexpr byte ButtonPins[] {A0}; // portPin o---|button|---GND
constexpr byte LedPins[] {9}; // portPin o---|220|---|LED|---GND
// CONSTANT DEFINITION
enum {One};
// VARIABLE DECLARATION AND DEFINITION
unsigned long currentTime;
struct TIMER { // has the following members
unsigned long duration; // memory for interval time
unsigned long stamp; // memory for actual time
bool onOff; // control for start/stop
};
struct BUTTON { // has the following members
byte pin; // port pin
bool statusQuo; // current state
TIMER scan; // see timer struct
};
struct BUTTON2LED {
byte led;
TIMER wait;
BUTTON knop;
};
BUTTON2LED button2Leds[] {
{LedPins[One], 150, 0, false, ButtonPins[One], false, 20, 0, true},
};
// -------------------------------------------------------------------
void setup() {
Serial.begin(9600);
Serial.println(F("."));
Serial.print(F("File : ")), Serial.println(__FILE__);
Serial.print(F("Date : ")), Serial.println(__DATE__);
Serial.print(F("Project: ")), Serial.println(ProjectName);
pinMode (LED_BUILTIN, OUTPUT); // used as heartbeat indicator
// https://www.learncpp.com/cpp-tutorial/for-each-loops/
for (auto Input : ButtonPins) pinMode(Input, INPUT_PULLUP);
for (auto Output_ : LedPins) pinMode(Output_, OUTPUT);
}
void loop () {
currentTime = millis();
digitalWrite(LED_BUILTIN, (currentTime / 500) % 2);
for (auto &button2Led : button2Leds) {
if (currentTime - button2Led.knop.scan.stamp >= button2Led.knop.scan.duration) {
button2Led.knop.scan.stamp = currentTime;
int stateNew = !digitalRead(button2Led.knop.pin);
if (button2Led.knop.statusQuo != stateNew) {
button2Led.knop.statusQuo = stateNew;
if (stateNew) digitalWrite( button2Led.led, HIGH), button2Led.wait.onOff = true, button2Led.wait.stamp = currentTime;
}
if (currentTime - button2Led.wait.stamp >= button2Led.wait.duration && button2Led.wait.onOff )button2Led.wait.onOff = false, digitalWrite( button2Led.led, LOW);
}
}
}
Have a nice day, enjoy coding in C++ and feel free to reply for any help.
That's of course a valid comment (also that it might only be an issue in situations where one goes really down to the millisecond).
I have changed my example to take care of this (only once taking millis() per loop()), including automatically switching onTime and offTime as well as a possible offset for each single valve. The Wokwi uses two Leds instead of valves ... Could be done more sophisticated, but may show a proof of concept:
struct ValveType {
unsigned long LastMillis = 0;
unsigned long interval = 150;
unsigned long onTime = 150;
unsigned long offTime = 400;
unsigned long offSet = 0;
boolean isOn = false;
int Pin;
};
const int NoOfValves = 2;
ValveType Valve[NoOfValves];
void SwitchIntervalOfValve(int No){
if (Valve[No].isOn) Valve[No].interval = Valve[No].onTime + Valve[No].offSet;
else Valve[No].interval = Valve[No].offTime+ Valve[No].offSet;
}
void SetValve(int No, int State) {
Valve[No].isOn = State;
SwitchIntervalOfValve(No);
digitalWrite(Valve[No].Pin, Valve[No].isOn);
}
void InitValve(int No, int State, unsigned long offset = 0) {
Valve[No].offSet = offset;
SetValve(No, State);
}
void SwitchValveNo(int No, unsigned long now) {
if (now - Valve[No].LastMillis > Valve[No].interval) {
Valve[No].LastMillis = now;
Valve[No].isOn = !Valve[No].isOn;
SetValve(No, Valve[No].isOn);
}
}
void SwitchValves() {
unsigned long now = millis();
for (int i = 0; i < NoOfValves; i++ ) SwitchValveNo(i, now);
}
void setup() {
Serial.begin(115200);
// Just a quick initialization
for (int i = 0; i < NoOfValves; i++ ) {
Valve[i].Pin = 9 + i; // Only valid for max. 5 "Valves" = Leds, as 9 + 4 = 13 (Pin 13)
pinMode(Valve[i].Pin, OUTPUT);
}
InitValve(0,LOW,0);
InitValve(1,LOW,200);
}
void loop() {
SwitchValves();
}