Red for 4s, green for 6s, yellow for 1.5s, round again. A button cuts green short, but only once cars have had a fair 1.5s. Same edge detection as Button Toggle, just with three branches instead of one. Once the pattern clicked the extra states were free.
Why delay() had to go
With delay() in the loop the board is asleep for seconds at a time, and a press
during the wait is simply gone. Nothing is listening. So instead of waiting, the
loop stamps the time it entered a state and works out elapsed = millis() - stateStart fresh on every pass. Every case then does the same two things:
drive the LEDs for this state, then check whether it is time to leave, and if so
change state and re-stamp stateStart.
The subtraction order matters more than it looks. millis() wraps back to 0
after about 49.7 days. now - start stays correct across that wrap because
unsigned arithmetic wraps too. now >= start + duration does not, because
start + duration can overflow and become a tiny number, and then the condition
either fires instantly or never fires again. Same maths, one version breaks
silently a month and a half in.
What I learned
- A C++
enumis just an integer wearing a nametag.RED,YELLOW,GREENare really 0, 1, 2 enum classmakes it scoped, so the names live insideLightand it will not silently convert to an int.: uint8_ttells the compiler to keep it in one byte::is the scope resolution operator. Java'sLight.REDis C++'sLight::RED- Always
now - start >= duration, nevernow >= start + duration. The second one breaks at themillis()rollover - Stamp the start time on entering a state.
elapsedhas to be measured from when this state began - I first justified that re-stamp as skipping the Arduino bootup time. Wrong reason, bootup is a rounding error. The real one is that without it every state after the first compares against time-since-power-on and fires immediately
- With just
if (pressed)that branch would fire about 50,000 times a second. Edge detection turns a continuous condition into a discrete event walkRequestedis the seam. The button code and the traffic code never talk to each other, one sets the flag and the other consumes it. Sensor code and control code should not be entangled- Clear the flag when the request is satisfied, here the green to yellow transition. Leave it set and the next green gets cut short by a press nobody made. Where "satisfied" is turns out to move, which is the catch P04b finds
- Polling the button every single iteration is the whole point
boolintodigitalWriteworks becauseHIGH/trueis 1 andLOW/falseis 0, which is what letssetLight(bool, bool, bool)take plain true and false- Nothing blocks, so
loop()could run several timed behaviours at once. That is the actual payoff, not the traffic light