Take some time and search for a led sequencer sketch.
Do you have experience with programming in C++.
The task can easily be realised with an object.
A structured array contains all the information, such as the pin addresses for the I/O devices, as well as the information for the timing.
A single service takes care of this information and initiates the intended action.
The structured array makes the sketch scalable until all I/O pins are used up without having to adapt the code for the service.
It is cool stuff, isn´t it?
Your project will be in one of 4 states at any time. In each state a different combination of lights will be on/off
Take a look at the switch/case functionality for a neat way to run a different block of code relevant to the current state
Now to the timing. Before each state starts save the value of millis() as the start time. Then, when in the code block for the current state, each time through loop() check whether the current value of millis() minus the start time of the state previously recorded equals or is greater than the period for the current state. If no, then keep going round loop() until the period passes.
When the current period ends change the state of the lights ready for the next state, save the start time of the next state and change the variable being used in switch() to that of the next state so that the code block for that state is executed next time through loop()
such traffic-lights can be seen as a device with different modes of operation:
each mode of operation is a certain pattern of lights switched on/off
like shown in your picture
So you have 4 modes of operation and transisitioning from one mode to the next needs a certain time
adding the waitings you get 8 modes of operation
mode 1: switch on only red light
mode 2: wait 5 seconds with only red light on
mode 3: switch on yellow light
mode 4: wait 2 seconds with red/yellow light on
mode 5: switch off red/yellow switch on green
mode 6: wait 10 seconds with green light switched on
mode 7: switch off green light switch on yellow light
mode 8: wait 2 seconds with yellow light on
if mode 8 has finished set mode 1
using a state-machine with for each combination of lights
is the ideal thing to code something like this
take a look at the example and adapt it to your modes
The state-machine-approach enables that the code will react very fast to any button-press if you add walking lights for people walking and press the "request for green"-button
There is no "need" t use delay() either, it's just a coding choice.
hey you could use millis with your approach too
void loop() {
uint32_t now;
digitalWrite(13, HIGH);
now = millis(); while (millis() - now <= 5000) yield();
digitalWrite(12, HIGH);
now = millis(); while (millis() - now <= 2000) yield();
digitalWrite(13, LOW);
digitalWrite(12, LOW);
digitalWrite(11, HIGH);
now = millis(); while (millis() - now <= 10000) yield();
digitalWrite(11, LOW);
digitalWrite(12, HIGH);
now = millis(); while (millis() - now <= 2000) yield();
}
It's to make the project bigger in the future, where I do not want the code to simply WAIT until the delay function has ran through its time. So that's why
State-machines sound scary, but in fact, like millis, arrays and structs it can massively simplify sequential coding projects, but I understand your concern.
Complete multi route traffic light controller becomes very complicated very quickly, and without these tools, becomes unmanageable in quick time !
I was just surprised they didn’t get a mention early-on !
Some sequencers or selectors are devolved state machines. A selector is a state machine with only two states, uses one boolean value as a state variable. A sequencer, multiple states, uses one integer variable to represent all possible states.
Lots of people make those, never realize that it's a "state machine". Because the states are not expressed as such, just implied in the logic.
They are still legitimate state machine implementations.
Just because the project might get bigger does not necessarily mean you need to use millis() instead of delay().
The point I'm trying to make is that knowing when and why to use a function is equally important as knowing how. If you know the how but not the when or why, your code gets more complex than it need to be, and that's a bad thing.