Este é um projeto simples para começar a aprender eletrónica e programação com o Arduino.
Hardware
- Computador
- Arduino UNO
- Cabo USB A macho para USB B macho
- Módulo de semáforo (ou LEDs x3 + resistências x3)
- Botão de pressão (ou módulo de botão de pressão)
- Prancheta
- Cabos Dupont M
- Botão de pressão x1
Objetivo
Vamos reproduzir o funcionamento de um semáforo vermelho. Em primeiro lugar, no modo automático, os LEDs acendem-se um após o outro. Depois, vamos adicionar um botão de chamada de peões para forçar o semáforo a ficar vermelho.
Preparação e teste do código para o Arduino
Sem ligar os LEDs, já podemos começar a trabalhar no algoritmo básico para testar a comunicação em série e a programação do Arduino. Pode seguir este tutorial para configurar e programar a 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étrico
O botão está ligado ao pino 2, o LED1 ao pino 9, o LED2 ao pino 10 e o LED3 ao pino 11. Tenha em atenção as resistências para não danificar os componentes. No módulo de semáforos, estas resistências não são necessárias.
![arduino-feu-tricolore_bb Reproduzir o funcionamento de um semáforo](https://www.aranacorp.com/wp-content/uploads/arduino-feu-tricolore_bb.png)
Código de gestão do LED
Assim que os LEDs estiverem ligados ao Arduino, podemos tentar ligá-los. A função utilizada para controlar um díodo é digitalWrite() com o parâmetro HIGH para o ligar e LOW para o desligar. Tudo o que tens de fazer é adicionar a função digitalWrite() à localização pretendida nas funções on e off.
O objetivo é fazer com que o LED verde se acenda, depois o LED laranja e depois o LED vermelho, após determinados intervalos de tempo.
Para facilitar a leitura, estamos a colocar as variáveis verde, laranja e vermelho num 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; } }
A luminosidade do LED pode ser modulada utilizando a função analogWrite() e uma saída PWM do microcontrolador.
Código de gestão dos botões
Vamos agora testar o funcionamento do botão de pressão. O objetivo é que o semáforo mude de cor quando o botão é premido. Para o conseguir, precisamos de “ouvir” constantemente o estado do botão. Uma técnica interessante para monitorizar o estado de um sensor é utilizar uma interrupção. Voltemos então ao código deste tutorial sobre a gestão de um botão com uma interrupção.
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 outras formas de gerir o botão de pressão, pode ler este tutorial.
Alterar a cor da luz utilizando o botão
Vamos combinar os dois códigos anteriores para mudar a cor do semáforo quando o botão é premido.
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; } }
Se testar este código, vai aperceber-se de que o semáforo não se comporta como esperado. Conseguimos forçar a cor vermelha, mas o funcionamento automático não é retomado corretamente. Isto deve-se ao facto de a função delay() bloquear o código. Para podermos ter em conta o botão quando o premimos, temos de modificar o código para nos livrarmos da função delay().
Alteração do código
Para remover a função delay(), podemos utilizar um algoritmo com a função millis(). Vamos reproduzir a função de atraso utilizando a função millis sem bloquear o fluxo de código.
if((millis()-previousTime)>200){ previousTime=currentTime; Serial.println(F("action")); }
Para isso, também precisamos de decompor o funcionamento da luz vermelha em diferentes estados e integrá-los nos casos de comutação. Um estado para a mudança de cor e um estado para o atraso.
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
Uma vez efectuadas estas alterações, a pressão no botão é imediatamente tida em conta e o semáforo pode retomar o seu 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; } }
Não hesite em deixar um comentário para partilhar as suas realizações ou dar-nos a sua opinião. Também pode contactar-nos se tiver alguma dúvida.
Aplicação
- Reproduzir uma passagem de nível com 4 semáforos sincronizados