I think the state change detection as typically presented is harder to understand, or at least it is easier once a few things get learnt.
After the check for a change in the input, if there is one we can deduce that a pressed button must just have become pressed, and similarly an unpressed button must just have been released.
But we use the state as proxy for those events. Properly, from a logic point of view, but confusing.
Borrowing from some code I saw that handled multiple inputs and recasting it to exemplify the code for this using just one button, I wrote the following code. It examines the switch and calculates a boolean value for each edge, leading or trailing.
The code can then test and use those more meaningful variables directly.
I did not include code for the OP's requirment.
Try it here:
UA State Change Flags
// https://wokwi.com/projects/408924267717447681
// https://forum.arduino.cc/t/making-a-led-switch-but-its-a-bit-more-complex/1300283
const byte buttonPin = 2;
const byte toggleLED0 = 12;
const byte toggleLED1 = 11;
# define PRESST LOW
void setup() {
Serial.begin(115200);
Serial.println("Ready.\n");
pinMode(buttonPin, INPUT_PULLUP);
pinMode(toggleLED0, OUTPUT);
pinMode(toggleLED1, OUTPUT);
}
bool prevousButton; // true means button is pressed. down. shorted.
void loop() {
bool thisButton = digitalRead(buttonPin) == PRESST; // true if switch is closed
// detect state change?
if (thisButton != prevousButton) {
// yes the button is different to last time, set flags
bool leadingEdge = thisButton && !prevousButton; // button is down and wasn't, became pressed
bool trailingEdge = !thisButton && prevousButton; // button is up and wasn't, became released
// record for state change detection
prevousButton = thisButton;
// just toggle. one on the leading edge
if (leadingEdge)
digitalWrite(toggleLED0, digitalRead(toggleLED0) == HIGH ? LOW : HIGH);
// and the other one on the trolling edge
if (trailingEdge)
digitalWrite(toggleLED1, digitalRead(toggleLED1) == HIGH ? LOW : HIGH);
delay(50); // poor man's debounce. only when the state change is done
}
}
Careful readers will point out that both leadingEdge and trailingEdge cannot be true at once. At the cost of whatever it does, I leave it untagnled, so each event is the same, and any use of the event always is expressly referring to the flag, not the opposite of something tested previously with the code in an else block.
Multiple uses of either can be in one block or not; in theOP's requirement this lead me to have the thing that marks every other press separate to the logic that does the odd on with a leading edge and off with the second next trailing edge. It would be the same in my flag version, just referring to the edges and not the state of the button. Which, again, is correct but you have to think about it.
a7