Totalmente ajustables los tiempos de cada color, ideado para utilizar LED RGB pero se puede utilizar LED normal amplificando la corriente de salida utilizando una interfaz de potencia, ya sea con BJT o con relés, ambas mostradas en este portal.

El código es el siguiente:

// ================= PINES =================
#define LED_ROJO 13
#define LED_AM_R 7
#define LED_AM_G 6
#define LED_VD_R 5
#define LED_VD_G 4
#define BTN_PIN 2

// ================ TIEMPOS ================
#define T_ROJO 7000UL
#define T_VERDE 7000UL
#define T_AMARILLO 3000UL
#define DEBOUNCE 250UL

// ============== ESTADOS ==================
enum Estado {
ROJO,
AMARILLO_1,
VERDE,
AMARILLO_2
};

Estado estadoActual = ROJO;
unsigned long tiempoEstado = 0;

// ============= PULSADOR ==================
bool modoForzado = false;
bool btnAnterior = LOW;
unsigned long tiempoBtn = 0;

// ============= FUNCIONES =================
void apagarTodo() {
digitalWrite(LED_ROJO, LOW);
digitalWrite(LED_AM_R, LOW);
digitalWrite(LED_AM_G, LOW);
digitalWrite(LED_VD_R, LOW);
digitalWrite(LED_VD_G, LOW);
}

void mostrarEstado(Estado e) {
apagarTodo();

switch (e) {
case ROJO:
digitalWrite(LED_ROJO, HIGH);
break;

case AMARILLO_1:
case AMARILLO_2:
digitalWrite(LED_AM_R, HIGH);
digitalWrite(LED_AM_G, HIGH);
break;

case VERDE:
digitalWrite(LED_VD_G, HIGH);
break;
}
}

void forzarRojo() {
apagarTodo();
digitalWrite(LED_ROJO, HIGH);
digitalWrite(LED_AM_R, HIGH);
digitalWrite(LED_VD_R, HIGH);
}

// ================ SETUP ==================
void setup() {
pinMode(LED_ROJO, OUTPUT);
pinMode(LED_AM_R, OUTPUT);
pinMode(LED_AM_G, OUTPUT);
pinMode(LED_VD_R, OUTPUT);
pinMode(LED_VD_G, OUTPUT);

pinMode(BTN_PIN, INPUT); // pull-down externo

tiempoEstado = millis();
mostrarEstado(estadoActual);
}

// ================= LOOP ==================
void loop() {
unsigned long ahora = millis();

// ----------- LECTURA DEL BOTÓN ----------
bool btnActual = digitalRead(BTN_PIN);

if (btnActual != btnAnterior) {
tiempoBtn = ahora;
btnAnterior = btnActual;
}

if ((ahora - tiempoBtn) >= DEBOUNCE && btnActual == HIGH) {
modoForzado = !modoForzado;
tiempoBtn = ahora;
}

// ----------- MODO FORZADO ---------------
if (modoForzado) {
forzarRojo();
return; // congela la máquina de estados
}

// ----------- MAQUINA DE ESTADOS ---------
unsigned long duracion = 0;

switch (estadoActual) {
case ROJO: duracion = T_ROJO; break;
case VERDE: duracion = T_VERDE; break;
default: duracion = T_AMARILLO; break;
}

if (ahora - tiempoEstado >= duracion) {
tiempoEstado = ahora;

switch (estadoActual) {
case ROJO: estadoActual = AMARILLO_1; break;
case AMARILLO_1: estadoActual = VERDE; break;
case VERDE: estadoActual = AMARILLO_2; break;
case AMARILLO_2: estadoActual = ROJO; break;
}

mostrarEstado(estadoActual);
}
}

SEMAFORO.INO