#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// Dimensiones del OLED
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
// Dirección del OLED en I2C (comúnmente 0x3C)
#define OLED_ADDR 0x3C
// Inicialización del objeto display
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);
// Pines de LEDs
const int LED_R_Princi = 3; // Rojo vía principal
const int LED_Y_Princi = 4;// Amarillo vía principal
const int LED_G_Princi = 5;// Verde vía principal
const int LED_R_SEC = 6;// Rojo vía secundaria
const int LED_Y_SEC = 7; // Amarillo vía secundaria
const int LED_G_SEC = 8; // Verde vía secundaria
const int LED_R_Peat = 9; // Rojo peatonal
const int LED_G_Peat = 10; // Verde peatonal
const int LED_Peat_Solicitud = 11; // Luz de solicitud peatonal
const int BUTTON_Peat = 2; // Botón peatonal
// Tiempos en milisegundos
const int tRESET = 3000;
const int tVERDE1 = 18000;
const int tAMARILLO1 = 4000;
const int tVERDE2 = 10000;
const int tAMARILLO2 = 3000;
const int tVERDE3 = 5000;
const int tROJO = 2000;
//declara la función
void encenderLEDs(int rojoPrinci, int rojoSec, int rojoPeat, int verdePrinci, int amarilloPrinci, int verdeSec, int amarilloSec);
// Máquina de estados o ciclo
enum State {
RESET,
VERDE_Princi,
AMARILLO_Princi,
VERDE_SEC,
AMARILLO_SEC,
VERDE_Peat,
ROJO_Peat
};
State currentState = RESET; //Estado inicial de la máquina
unsigned long previousMillis = 0; // Control del tiempo
bool PeatSolicitud = false; // Solicitud peatonal, bandera activa
void setup() {
pinMode(LED_R_Princi, OUTPUT);
pinMode(LED_Y_Princi, OUTPUT);
pinMode(LED_G_Princi, OUTPUT);
pinMode(LED_R_SEC, OUTPUT);
pinMode(LED_Y_SEC, OUTPUT);
pinMode(LED_G_SEC, OUTPUT);
pinMode(LED_R_Peat, OUTPUT);
pinMode(LED_G_Peat, OUTPUT);
pinMode(BUTTON_Peat, INPUT_PULLUP);
pinMode(LED_Peat_Solicitud, OUTPUT);
if (!display.begin(SSD1306_PAGEADDR, OLED_ADDR)) { //intenta inicializar el display OLED. Si la inicialización es exitosa, devuelve true. Si falla, devuelve false.
for (;;);
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.display();
}
void loop() {
unsigned long currentMillis = millis();
//Veriicamos si el botón ha sido presionado por confg, PULLUP, no reset, o verde peaton
if (digitalRead(BUTTON_Peat) == LOW && !PeatSolicitud && currentState != RESET && currentState != VERDE_Peat) {
PeatSolicitud = true;
digitalWrite(LED_Peat_Solicitud, HIGH);
}
//Orden de la máquina de estados
switch (currentState) {
case RESET:
resetState(currentMillis);
break;
case VERDE_Princi:
verdePrinciState(currentMillis);
break;
case AMARILLO_Princi:
amarilloPrinciState(currentMillis);
break;
case VERDE_SEC:
verdeSecState(currentMillis);
break;
case AMARILLO_SEC:
amarilloSecState(currentMillis);
break;
case VERDE_Peat:
verdePeatState(currentMillis);
break;
case ROJO_Peat:
rojoPeatState(currentMillis);
break;
}
}
void resetState(unsigned long currentMillis) {
int timeLeft = tRESET - (currentMillis - previousMillis);
if (timeLeft <= 0) { //tiempo transcurrido, pasa al sig estado
previousMillis = currentMillis;
currentState = VERDE_Princi;
}
encenderLEDs(HIGH, HIGH, HIGH, LOW, LOW, LOW, LOW);
displayMensaje("Estado: \nRESET", timeLeft);
}
void verdePrinciState(unsigned long currentMillis) {
int timeLeft = tVERDE1 - (currentMillis - previousMillis); //Calcula el tiempo restante en el estado actual
if (timeLeft <= 0) {
previousMillis = currentMillis;
currentState = AMARILLO_Princi;
}
encenderLEDs(LOW, HIGH, HIGH, HIGH, LOW, LOW, LOW);
displayMensaje("Estado: \nVerde Principal", timeLeft);
}
void amarilloPrinciState(unsigned long currentMillis) {
int timeLeft = tAMARILLO1 - (currentMillis - previousMillis);
if (timeLeft <= 0) {
previousMillis = currentMillis;
currentState = VERDE_SEC;
}
encenderLEDs(LOW, HIGH, HIGH, LOW, HIGH, LOW, LOW);
displayMensaje("Estado: \nAmarillo Principal", timeLeft);
}
void verdeSecState(unsigned long currentMillis) {
int timeLeft = tVERDE2 - (currentMillis - previousMillis);
if (timeLeft <= 0) {
previousMillis = currentMillis;
currentState = AMARILLO_SEC;
}
encenderLEDs(HIGH, LOW, HIGH, LOW, LOW, HIGH, LOW);
displayMensaje("Estado: \nVerde Secundario", timeLeft);
}
void amarilloSecState(unsigned long currentMillis) {
int timeLeft = tAMARILLO2 - (currentMillis - previousMillis);
if (timeLeft <= 0) {
previousMillis = currentMillis;
currentState = (PeatSolicitud ? VERDE_Peat : VERDE_Princi); //Verifica si bandera peaton activa
}
encenderLEDs(HIGH, LOW, HIGH, LOW, LOW, LOW, HIGH);
displayMensaje("Estado: \nAmarillo Secundario", timeLeft);
}
void verdePeatState(unsigned long currentMillis) {
int timeLeft = tVERDE3 - (currentMillis - previousMillis);
if (timeLeft <= 0) {
previousMillis = currentMillis;
currentState = ROJO_Peat;
}
encenderLEDs(HIGH, HIGH, LOW, LOW, LOW, LOW, LOW);
digitalWrite(LED_G_Peat, HIGH);
displayMensaje("Estado: \nVerde Peatonal", timeLeft);
}
void rojoPeatState(unsigned long currentMillis) {
int timeLeft = tROJO - (currentMillis - previousMillis);
if (timeLeft <= 0) {
previousMillis = currentMillis;
currentState = VERDE_Princi;
PeatSolicitud = false;
digitalWrite(LED_Peat_Solicitud, LOW);
}
encenderLEDs(HIGH, HIGH, HIGH, LOW, LOW, LOW, LOW);
displayMensaje("Estado: \nRojo Peatonal", timeLeft);
}
void displayMensaje(const char *message, int timeLeft) {
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1);
display.println(message);
display.print("\nTiempo restante: ");
display.print((timeLeft > 0) ? timeLeft / 1000 : 0); //Seg a milliseg, si timeleft negativo, será 0
display.println("s");
display.display();
}
void encenderLEDs(int rojoPrinci, int rojoSec, int rojoPeat, int verdePrinci, int amarilloPrinci, int verdeSec, int amarilloSec) {
digitalWrite(LED_R_Princi, rojoPrinci);
digitalWrite(LED_R_SEC, rojoSec);
digitalWrite(LED_R_Peat, rojoPeat);
digitalWrite(LED_G_Princi, verdePrinci);
digitalWrite(LED_Y_Princi, amarilloPrinci);
digitalWrite(LED_G_SEC, verdeSec);
digitalWrite(LED_Y_SEC, amarilloSec); // Control del amarillo del semáforo secundario
digitalWrite(LED_G_Peat, LOW); // Apagar verde peatonal si no está en uso
}