I slightly modified @6v6gt's code to be non-blocking. This allows standing up a traffic lights sequencer beside that code to make a fully automatic demo.
Since life is so short, I severely cut down all the time constants. It should work fine with the numbers changed to reflect real life timing.
In the wokwi, there is one function call in loop() and one in setup() to implement the traffic lights. In the schematic, the squiggly orange wire is the only connection between the countdown stuff and the traffic lights sequencer.
Play with it here:
traffic and lights
Code. I had to change the sense of the input to @6v6gt's algorithm. I also notice that just looking at the red light results in some potential flaws arising. Here, for example, I note that the countdown number remains at zero, and on display, during the RED/AMBER period.
// https://wokwi.com/projects/375974332983806977
// https://forum.arduino.cc/t/traffic-light-time-to-green-count-down/1168737
#include <LiquidCrystal_I2C.h>
const uint8_t redPin = 4 ;
const uint8_t ledPin = 7 ;
bool lastRedState = HIGH ;
uint16_t counter = 0 ;
uint32_t lastSecs = 0 ;
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup() {
Serial.begin(115200);
pinMode(redPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT );
lcd.init( );
lcd.backlight();
setupTrafficLights();
}
void loop() {
serviceTrafficLights();
// to only run the traffic lights, just return; right here
bool redState = !digitalRead( redPin ) ; // true logic now
uint32_t secs = millis() / 1000 ;
if ( redState == LOW ) {
digitalWrite(ledPin, HIGH);
if ( secs != lastSecs ) {
lastSecs = secs ;
if ( counter > 0 ) counter-- ;
}
if ( lastRedState == HIGH ) {
lastRedState = redState ;
counter = 15 ; // should be 130. Life too short
}
static uint16_t onDisaply = 999; // impossible previous value
// if ((counter < 100) && (counter != onDisaply)) {
if ((counter < 10) && (counter != onDisaply)) {
lcd.clear();
lcd.print( counter );
onDisaply = counter;
}
}
else {
// clear up
lcd.clear() ;
digitalWrite(ledPin, LOW);
lastRedState = HIGH ;
}
}
// code to create a set of traffic lights to inform the coutdown process
// non-blocked traffic light sequencer
// "Usual UK system of Red Red/Amber Green Amber "
// three outputs for the simulated traffic lights
const uint8_t redLight = 8;
const uint8_t yellowLight = 9;
const uint8_t greenLight = 10;
enum {RED, REDAMBER, GREEN, AMBER};
const unsigned long times[4] = {15000, 4000, 12000, 4000}; // in milliseconds here
void setupTrafficLights()
{
pinMode(redLight, OUTPUT);
pinMode(yellowLight, OUTPUT);
pinMode(greenLight, OUTPUT);
}
void serviceTrafficLights()
{
static unsigned char phase = 0;
static unsigned long waitFrom;
unsigned long now = millis();
if (waitFrom && now - waitFrom < times[phase]) return;
phase++; if (phase >= 4) phase = 0;
waitFrom = now;
switch(phase) {
case RED :
digitalWrite(redLight, HIGH);
digitalWrite(yellowLight, LOW);
digitalWrite(greenLight, LOW);
break;
case REDAMBER :
digitalWrite(redLight, HIGH);
digitalWrite(yellowLight, HIGH);
digitalWrite(greenLight, LOW);
break;
case GREEN :
digitalWrite(redLight, LOW);
digitalWrite(yellowLight, LOW);
digitalWrite(greenLight, HIGH);
break;
case AMBER :
digitalWrite(redLight, LOW);
digitalWrite(yellowLight, HIGH);
digitalWrite(greenLight, LOW);
break;
default :
// yes, this did show up, so
Serial.println("logic error somewhere");
}
}
a7