Para este proyecto, vamos a construir un semáforo y a reproducir su funcionamiento. Veremos paso a paso cómo probar cada componente y desarrollar el programa final.
Este es un proyecto sencillo para empezar a aprender electrónica y programación con Arduino.
Hardware
- Ordenador
- Arduino UNO
- Cable USB A macho a USB B macho
- Módulo semáforo (o LED x3 + resistencias x3)
- Pulsador (o módulo pulsador)
- Placa de pruebas
- Cables Dupont M
- Pulsador x1
Objetivo
Vamos a reproducir cómo funciona una luz roja. En primer lugar, en modo automático, los LED se encenderán uno tras otro. Después, añadiremos un botón de llamada al peatón para forzar que el semáforo se ponga en rojo.
Preparación y código de prueba para el Arduino
Sin conectar los LEDs, ya podemos empezar a trabajar en el algoritmo básico para probar la comunicación serie y la programación del Arduino. Puedes seguir este tutorial para configurar y programar la placa Arduino.
int verte = 0; int orange = 1; int rouge = 2; void setup() { Serial.begin(9600); Serial.println("Feux rouge initialisé"); } void loop() { Serial.println("loop running"); delay(500); allumer(verte); delay(1000); eteindre(verte); allumer(orange); delay(1000); eteindre(orange); allumer(rouge); delay(1000); } void allumer(int color) { Serial.print("allumer led "); switch (color) { case 0: Serial.println("verte"); break; case 1: Serial.println("orange"); break; case 2: Serial.println("rouge"); break; } } void eteindre(int color) { Serial.print("eteindre led :"); switch (color) { case 0: Serial.println("verte"); break; case 1: Serial.println("orange"); break; case 2: Serial.println("rouge"); break; } }
Esquema eléctrico
El pulsador se conecta a la patilla 2, el LED1 a la patilla 9, el LED2 a la patilla 10 y el LED3 a la patilla 11. Tenga en cuenta las resistencias para no dañar los componentes. Con el módulo semáforo, estas resistencias no son necesarias.
Código de gestión del LED
Una vez conectados los diodos al Arduino, podemos intentar encenderlos. La función utilizada para controlar un diodo es digitalWrite() con el parámetro HIGH para encenderlo, LOW para apagarlo. Todo lo que tienes que hacer es añadir la función digitalWrite() en el lugar deseado en las funciones de encendido y apagado.
El objetivo es hacer que se encienda el LED verde, luego el naranja y después el rojo tras intervalos de tiempo determinados.
Para facilitar la lectura, vamos a colocar las variables verde, naranja y rojo en un enum.
const int pinVerte = 9; const int pinOrange = 10; const int pinRouge = 11; enum couleur { VERTE, ORANGE, ROUGE }; int etatFeu = VERTE; void setup() { Serial.begin(9600); pinMode(pinVerte, OUTPUT); pinMode(pinRouge, OUTPUT); pinMode(pinOrange, OUTPUT); Serial.println("Feu tricolore initialisé"); } void loop() { Serial.println(F("Mode auto running")); eteindre (ROUGE); allumer (VERTE); delay (5000); eteindre (VERTE); allumer (ORANGE); delay (1000); eteindre (ORANGE); allumer (ROUGE); delay (3000); } void allumer(int color) { Serial.print("allumer led "); switch (color) { case VERTE: Serial.println("verte"); digitalWrite(pinVerte, HIGH); //analogWrite(pinVerte, 250); break; case ORANGE: Serial.println("orange"); digitalWrite(pinOrange, HIGH); //analogWrite(pinOrange, 250); break; case ROUGE: Serial.println("rouge"); digitalWrite(pinRouge, HIGH); //analogWrite(pinRouge, 250); break; default: digitalWrite(pinOrange, HIGH); //analogWrite(pinOrange, 250); break; } etatFeu = color; } void eteindre(int color) { Serial.print("eteindre led :"); switch (color) { case VERTE: Serial.println("verte"); digitalWrite(pinVerte, LOW); //analogWrite(pinVerte, 0); break; case ORANGE: Serial.println("orange"); digitalWrite(pinOrange, LOW); //analogWrite(pinOrange, 0); break; case ROUGE: Serial.println("rouge"); digitalWrite(pinRouge, LOW); //analogWrite(pinRouge, 0); break; default: digitalWrite(pinVerte, LOW); //analogWrite(pinVerte, 0); digitalWrite(pinOrange, LOW); //analogWrite(pinOrange, 0); // digitalWrite(pinRouge, LOW); //analogWrite(pinRouge, 0); // break; } }
El brillo del LED puede modularse utilizando la función analogWrite() y una salida PWM del microcontrolador.
Código de gestión de botones
Ahora vamos a probar cómo funciona el pulsador. El objetivo es que el semáforo cambie de color al pulsar el botón. Para conseguirlo, necesitamos «escuchar» constantemente el estado del pulsador. Una técnica interesante para monitorizar el estado de un sensor es utilizar una interrupción. Así que volvamos al código de este tutorial sobre la gestión de un botón con una interrupción.
const byte ledPin = 10; const byte interruptPin = 2; volatile byte state = LOW; void setup() { Serial.begin(9600); pinMode(ledPin, OUTPUT); pinMode(interruptPin, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(interruptPin), onEvent, FALLING); Serial.println(F("Système intialisé")); } void loop() { digitalWrite(ledPin, state); } void onEvent() { state = !state; Serial.print(F("Switch LED : ")); if (state) { Serial.println(F("ON")); } else { Serial.println(F("OFF")); } }
Para otras formas de manejar el pulsador, puedes leer este tutorial.
Cambia el color de la luz con el botón
Vamos a combinar los dos códigos anteriores para cambiar el color del semáforo al pulsar el botón.
const int pinVerte = 9; const int pinOrange = 10; const int pinRouge = 11; enum couleur { VERTE, ORANGE, ROUGE }; int etatFeu = VERTE; const byte btnPin = 2; int state = 0; void setup() { Serial.begin(9600); pinMode(pinVerte, OUTPUT); pinMode(pinRouge, OUTPUT); pinMode(pinOrange, OUTPUT); pinMode(btnPin, INPUT); attachInterrupt(digitalPinToInterrupt(btnPin), onEvent, FALLING); Serial.println("Feu tricolore initialisé"); } void loop() { switch (state) { case 0://mode automatique Serial.println(F("Mode auto")); eteindre (ROUGE); allumer (VERTE); delay (5000); eteindre (VERTE); allumer (ORANGE); delay (1000); eteindre (ORANGE); allumer (ROUGE); delay (3000); break; case 1:// mode manuel Serial.println(("Mode manuel")); delay (5000); eteindre (ROUGE); state = 0; break; } } void onEvent() { Serial.println("---> Bouton pressé"); eteindre(3);//eteindre tout allumer (ROUGE); state=1; } void allumer(int color) { Serial.print("allumer led "); switch (color) { case VERTE: Serial.println("verte"); digitalWrite(pinVerte, HIGH); //analogWrite(pinVerte, 250); break; case ORANGE: Serial.println("orange"); digitalWrite(pinOrange, HIGH); //analogWrite(pinOrange, 250); break; case ROUGE: Serial.println("rouge"); digitalWrite(pinRouge, HIGH); //analogWrite(pinRouge, 250); break; default: digitalWrite(pinOrange, HIGH); //analogWrite(pinOrange, 250); break; } } void eteindre(int color) { Serial.print("eteindre led : "); switch (color) { case VERTE: Serial.println("verte"); digitalWrite(pinVerte, LOW); //analogWrite(pinVerte, 0); break; case ORANGE: Serial.println("orange"); digitalWrite(pinOrange, LOW); //analogWrite(pinOrange, 0); break; case ROUGE: Serial.println("rouge"); digitalWrite(pinRouge, LOW); //analogWrite(pinRouge, 0); break; default: Serial.println(" tout"); digitalWrite(pinVerte, LOW); //analogWrite(pinVerte, 0); digitalWrite(pinOrange, LOW); //analogWrite(pinOrange, 0); // digitalWrite(pinRouge, LOW); //analogWrite(pinRouge, 0); // break; } }
Si pruebas este código, te darás cuenta de que el semáforo no se comporta como se esperaba. Conseguimos forzar el color a rojo pero el funcionamiento automático no se reanuda correctamente. Esto se debe a que la función delay() bloquea el código. Para poder tener en cuenta el botón cuando lo pulsamos, tenemos que modificar el código para eliminar la función delay().
Modificación del código
Para eliminar la función delay(), podemos utilizar un algoritmo con la función millis(). Vamos a reproducir la función delay utilizando la función millis sin bloquear el flujo de código.
if((millis()-previousTime)>200){ previousTime=currentTime; Serial.println(F("action")); }
Para ello, también tenemos que descomponer el funcionamiento de la luz roja en diferentes estados e integrarlos en los casos de conmutación. Un estado para el cambio de color y otro para el retardo.
switch (state) { case 0: eteindre (ROUGE); allumer (VERTE); state++; previousMillis = millis(); break; case 1: if (millis() - previousMillis > 5000) { //delay (5000); state++; } break; case 2: eteindre (VERTE); allumer (ORANGE); state++; previousMillis = millis(); break; case 3: if (millis() - previousMillis > 1000) { //delay (1000); state++; } break; case 4: eteindre (ORANGE); allumer (ROUGE); state++; previousMillis = millis(); break; case 5: if (millis() - previousMillis > 3000) { //delay (3000); state = 0; } break; }
Código final completo
Una vez realizados estos cambios, la pulsación del botón se tiene en cuenta inmediatamente y el semáforo puede reanudar su curso normal.
const int pinVerte = 9; const int pinOrange = 10; const int pinRouge = 11; enum couleur { VERTE, ORANGE, ROUGE }; int etatFeu = VERTE; int state = 0; const byte btnPin = 2; int mode = 0; unsigned long previousMillis = 0; void setup() { Serial.begin(9600); pinMode(pinVerte, OUTPUT); pinMode(pinRouge, OUTPUT); pinMode(pinOrange, OUTPUT); pinMode(btnPin, INPUT); attachInterrupt(digitalPinToInterrupt(btnPin), onEvent, RISING); Serial.println("Feu tricolore initialisé"); } void loop() { switch (state) { case 0: eteindre (ROUGE); allumer (VERTE); state++; previousMillis = millis(); break; case 1: if (millis() - previousMillis > 5000) { //delay (5000); state++; } break; case 2: eteindre (VERTE); allumer (ORANGE); state++; previousMillis = millis(); break; case 3: if (millis() - previousMillis > 1000) { //delay (1000); state++; } break; case 4: eteindre (ORANGE); allumer (ROUGE); state++; previousMillis = millis(); break; case 5: if (millis() - previousMillis > 3000) { //delay (3000); state = 0; } break; } } void onEvent() { Serial.println("---> Bouton pressé"); if (etatFeu == VERTE) { Serial.println("Changement d'etat"); state = 2; } } void allumer(int color) { Serial.print("allumer led "); switch (color) { case VERTE: Serial.println("verte"); digitalWrite(pinVerte, HIGH); //analogWrite(pinVerte, 250); break; case ORANGE: Serial.println("orange"); digitalWrite(pinOrange, HIGH); //analogWrite(pinOrange, 250); break; case ROUGE: Serial.println("rouge"); digitalWrite(pinRouge, HIGH); //analogWrite(pinRouge, 250); break; default: digitalWrite(pinOrange, HIGH); //analogWrite(pinOrange, 250); break; } etatFeu = color; } void eteindre(int color) { Serial.print("eteindre led :"); switch (color) { case VERTE: Serial.println("verte"); digitalWrite(pinVerte, LOW); //analogWrite(pinVerte, 0); break; case ORANGE: Serial.println("orange"); digitalWrite(pinOrange, LOW); //analogWrite(pinOrange, 0); break; case ROUGE: Serial.println("rouge"); digitalWrite(pinRouge, LOW); //analogWrite(pinRouge, 0); break; default: digitalWrite(pinVerte, LOW); //analogWrite(pinVerte, 0); digitalWrite(pinOrange, LOW); //analogWrite(pinOrange, 0); // digitalWrite(pinRouge, LOW); //analogWrite(pinRouge, 0); // break; } }
- Reproducir un cruce de carretera con 4 semáforos sincronizados