Semáforo

Hola.

Soy nuevo en esto de Arduino, y aprendiendo cosillas he decidido intentar hacer un semáforo. El caso es que aparentemente funciona todo correcto. Un semáforo que empieza en rojo, y pasado un tiempo se pone amarillo y verde y pasado el tiempo se vuelve a poner amarillo y rojo. El problema llega al poner un botón el cual debe hacer que si el semáforo está en rojo, pase a amarillo y verde automáticamente. Pues bien, resulta que si el botón lo pulso acabado de ponerse el semáforo en rojo funciona bien, pero si lo pulso cuando Arduino está leyendo la mitad del programa no hace caso al botón.

Os paso el código a ver si me podéis decir que puedo hacer. Entiendo que no funciona porque la función del botón está al principio del programa. ¿Cómo puedo decirle a Arduino que en cuanto reciba la orden del botón, se ponga en verde?

No busco opiniones de si el programa se puede optimizar y demás, ya que seguramente que sí, pero entiendan que estoy empezando, ya habrá tiempo de hacerlo más bonito. Ahora mismo lo que busco es que funcione. Quiero saber que se hace en los casos en que cuando se le envía una instrucción a Arduino, esté donde esté la lectura del programa, deje lo que está haciendo y se ponga con lo que se le manda.

Un saludo

int estado = 0; // Guarda el estado del botón int estadoAnterior = 0; // Guarda el estado anterior del botón int salida = 0; // 0 = LED Apagado, 1 = LED encendido int contador = 5000; // tiempo para que el semáforo cambie de color int tocahora = 0; // el semaforo empieza en rojo (pin 4) int ledRojo = 1; int ledVerde = 0;

void setup() { pinMode(8, INPUT); // Declaramos el botón como entrada pinMode(4, OUTPUT); // Declaramos el LED Rojo como salida pinMode(3, OUTPUT); // Declaramos el LED Amarillo como salida pinMode(2, OUTPUT); // Declaramos el LED Verde como salida

digitalWrite(4, HIGH); // enciende el led rojo }

void loop() {

estado = digitalRead(8); // Leer el estado del botón contador = contador - 1000;

if ((estado == HIGH) && (estadoAnterior == LOW) && (ledRojo = 1)) { // si el boton esta pulsado ,antes no estaba pulsado y el led está en rojo salida = 1 - salida;

delay(150); // espera 150ms para que los coches terminen de pasar digitalWrite(4, LOW); // Apaga led rojo digitalWrite(3, HIGH); // enciende led amarillo delay(25); // espera 25ms digitalWrite(3, LOW); // apaga led amarillo digitalWrite(2, HIGH); // enciende led verde delay(200); // espera 200ms para que pasen los peatones digitalWrite(2, LOW); // apaga led verde digitalWrite(3, HIGH); // enciende led amarillo delay(25); // espera 25ms digitalWrite(3, LOW); // apaga led amarillo digitalWrite(4, HIGH); // enciende led rojo

contador = 5000; }

if ((contador == 0 && ledRojo == 1)) { digitalWrite(4, LOW); digitalWrite(3, HIGH); delay(500); digitalWrite(3, LOW); digitalWrite(2, HIGH);

ledRojo = 0; ledVerde = 1; contador = 5000; }

if ((contador == 0 && ledVerde == 1)) { digitalWrite(2, LOW); digitalWrite(3, HIGH); delay(500); digitalWrite(3, LOW); digitalWrite(4, HIGH);

ledVerde = 0; ledRojo = 1; contador = 5000; }

delay(1000); // espera un segundo para pasar a verde

}

Hola! No creó que se pueda ponerlo una vez sola y se repita lo que tendrías que hacer es aplicar una condición para cada estado del semáforo, te doy un ejemplo:

if(Pulsador == HIGH){

if((LedRojo == HIGH) && (LedAmarillo == LOW) && (LedVerde == LOW){

digitalWrite(LedRojo,LOW); digitalWrite(LedAmarillo,HIGH); delay(500) digitalWrite(LedAmarillo,LOW); digitalwrite(LedVerde,HIGH); } }

Y tendrías que hacer así con cada estado del semáforo si el led amarillo esta prendido tendrías que preguntar si LedAmarillo == HIGH && LedRojo == LOW && LedVerde == LOW

digitalWrite(LedAmarillo, LOW) digitalWrite(LedVerde,HIGH)

En simples palabras tenes que aplicar una estructura de desicion para cada estado del semáforo

Ya está, lo solucioné. Lo que estaba pasando es que si pulsaba el botón mientras la lectura estaba en el delay(1000);, no le hacía caso, ya que lógicamente, está esperando los 1000ms que le había indicado.

La solución, meter un while para reducir esos 1000ms a 100ms. Ahora funciona correctamente. os dejo el código por si lo queréis ver.

Si queréis mañana os subo un esquema con Fritzing para que los que estén empezando sepan cómo va conectado todo y puedan probarlo.

int estado = 0; // Guarda el estado del botón int ledRojo = 1; int ledVerde = 0; int count = 99; // contador para que el semáforo cambie de color

void setup() { pinMode(8, INPUT); // Declaramos el botón como entrada pinMode(4, OUTPUT); // Declaramos el LED Rojo como salida pinMode(3, OUTPUT); // Declaramos el LED Amarillo como salida pinMode(2, OUTPUT); // Declaramos el LED Verde como salida

digitalWrite(4, HIGH); // enciende el led rojo }

void loop() {

while (count > 0) { // mientras el contador no llegue a cero, sigue en el color que estas estado = digitalRead(8); // Leer el estado del botón if ((estado == HIGH) && (ledRojo = 1)) { // si el boton esta pulsado y el led está en rojo count = count * 50; // Ahora el contador del semáforo se pone a la mitad delay(count); // El semáforo tardará la mitad en ponerse en verde digitalWrite(4, LOW); // Apaga led rojo digitalWrite(3, HIGH); // enciende led amarillo delay(500); // espera 500ms digitalWrite(3, LOW); // apaga led amarillo digitalWrite(2, HIGH); // enciende led verde delay(9900); // espera 9900ms para que pasen los peatones digitalWrite(2, LOW); // apaga led verde digitalWrite(3, HIGH); // enciende led amarillo delay(500); // espera 500ms digitalWrite(3, LOW); // apaga led amarillo digitalWrite(4, HIGH); // enciende led rojo count = 99; // resetea el contador a 99 } delay(100); // espera un segundo para pasar a verde count--; // disminuye la variable contador en 1 }

if ((count == 0 && ledRojo == 1)) { // si el contador llega a 0, y el led rojo está encendido, enciende el verde digitalWrite(4, LOW); digitalWrite(3, HIGH); delay(500); digitalWrite(3, LOW); digitalWrite(2, HIGH); ledRojo = 0; ledVerde = 1; count = 99; // resetea el contador a 99 }

if ((count == 0 && ledVerde == 1)) { // si el contador llega a 0, y el led verde está encendido, enciende el rojo digitalWrite(2, LOW); digitalWrite(3, HIGH); delay(500); digitalWrite(3, LOW); digitalWrite(4, HIGH); ledVerde = 0; ledRojo = 1; count = 99; // resetea el contador a 99 }

}

Para hacer lo que pretendes no puedes usar delay() porque interrumpes las situaciones en las que presionas el botón. Mira el ejemplo BlinkWithoutDelay.ino de tu IDE que usa millis() o busca en Documentación el tutorial de Max_saeta sobre el uso de millis().

Los delays deben reemplazarse por situaciones que usen millis() y además deberías usar una máquina de estados. La máquina de estados es la que establece en que situación debo seguir si ocurre algo... Digamos que si arranca tengo un estado1, 2, y 3 Estado1 = Rojo Estado2= Amarillo (De rojo a Verde) Estado3= Verde Estado4= Amarillo (De verde a Rojo) Estado5 = Pulsador

Puede que no estes de acuerdo con esta definción pero en Argentina asi son los estados Estado1 => Estado2 => Estado3 => Estado 4 => (Repite el ciclo) Estado1

El Estado5 seria el pulsador que de ser presionado debe enviar al Estado2

Cada estado lleva involucrado tiempos, esos tiempos no se mantienen con delay sino con millis y al terminarla demora que corresponda se activa el siguiente estado. Como millis() lleva el tiempo desde que se encendió el Arduino desde su arranque con cada estado debes inicializar una variable para que tome el valor de millis() en ese instante y puedas compararlo luego para determinar si han pasado por ejemplo 500mseg Asi se va armando toda la secuencia.

Claro que esta es una forma de hacerlo, hay muchas. Puedes usar timers bajo una lógica similar a la que he descripto.

Perfecto, gracias.

Aunque al final lo conseguí, me ha venido bien abrir el post para conocer el millis().

Un saludo.

El delay sirve cuando haces una secuencia repetitiva y no hay otras cuestiones a considerar porque cada delay desconecta el CPU del mundo.