Site icon AranaCorp

Reproduzir o funcionamento de um semáforo

Este é um projeto simples para começar a aprender eletrónica e programação com o Arduino.

Hardware

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.

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

Fonte

Exit mobile version