/*
13/4/26
SENAI - Serviço Nacional de Aprendizagem Industrial
Curso Técnico em Desenvolvimeno de Sistemas
Matéria: IoT (Internet das Coisas)
Docente: Carlos Giovani Dutra ([email protected])
Discente: Jean Douglas Toledo Rodrigues Junior ([email protected])
#PROJETO CANCELA
Depois do código seque documentação README.md
*/
#include <ESP32Servo.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <time.h>
const char* mqtt_server = "broker.hivemq.com";
const char* topic_cmd = "cancela/cmd";
const char* topic_status = "cancela/status";
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = -10800;
const int daylightOffset_sec = 0;
WiFiClient espClient;
PubSubClient client(espClient);
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const int trig1 = 16;
const int echo1 = 13;
const int trig2 = 14;
const int echo2 = 27;
#define PIN_RED 2
#define PIN_YELLOW 4
#define PIN_BLUE 5
#define BTN_RESET 25
#define BTN_EMERGENCY 26
#define SERVO_PIN 32
#define RELAY_PIN 33
Adafruit_SSD1306 display(128, 64, &Wire, -1);
Servo cancela;
bool emergencyMode = false;
bool barrierOpen = false;
unsigned long timerClose = 0;
unsigned long lastSensorRead = 0;
unsigned long lightOffTimer = 0;
unsigned long restartTimer = 0;
const int closeDelay = 5000;
const int distThreshold = 20;
bool lastEmergState = HIGH;
String lastActionMsg = "Pronto";
long d1 = 999;
long d2 = 999;
bool carAtS2 = false;
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("--- INICIANDO ---");
pinMode(PIN_RED, OUTPUT);
pinMode(PIN_YELLOW, OUTPUT);
pinMode(PIN_BLUE, OUTPUT);
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW);
pinMode(BTN_RESET, INPUT_PULLUP);
pinMode(BTN_EMERGENCY, INPUT_PULLUP);
pinMode(trig1, OUTPUT); pinMode(echo1, INPUT);
digitalWrite(trig1, LOW);
delay(200);
pinMode(trig2, OUTPUT); pinMode(echo2, INPUT);
digitalWrite(trig2, LOW);
delay(200);
Serial.println("Ligando Display...");
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("Aviso: OLED nao responde");
} else {
display.clearDisplay();
display.display();
}
delay(200);
Serial.println("Ligando Servo...");
cancela.setPeriodHertz(50);
cancela.attach(SERVO_PIN, 500, 2400);
cancela.write(0);
Serial.print("Conectando ao WiFi...");
WiFi.begin(ssid, password);
int retry = 0;
while (WiFi.status() != WL_CONNECTED && retry < 15) {
digitalWrite(PIN_BLUE, !digitalRead(PIN_BLUE));
delay(500);
Serial.print(".");
retry++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi Conectado!");
digitalWrite(PIN_BLUE, HIGH);
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
} else {
Serial.println("\nWiFi falhou (Modo Offline)");
digitalWrite(PIN_BLUE, LOW);
}
Serial.println("--- SETUP FINALIZADO ---");
}
bool isNightTime() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
return false;
}
int hour = timeinfo.tm_hour;
return (hour >= 19 || hour < 6);
}
void callback(char* topic, byte* payload, unsigned int length) {
String msg = "";
for (int i = 0; i < length; i++) msg += (char)payload[i];
Serial.print("MQTT ["); Serial.print(topic); Serial.print("]: "); Serial.println(msg);
if (msg == "abrir") {
openBarrier("App Abrir");
} else if (msg == "fechar") {
closeBarrier("App Fechar");
} else if (msg == "emerg") {
emergencyMode = !emergencyMode;
if (emergencyMode) {
lastActionMsg = "App Emerg! On";
if (isNightTime()) digitalWrite(RELAY_PIN, HIGH);
openBarrier("App Emerg! On");
} else {
lastActionMsg = "App Emerg! Off";
closeBarrier("App Emerg! Off");
}
} else if (msg == "reset") {
lastActionMsg = "App reset";
closeBarrier("App");
restartTimer = millis();
if (restartTimer == 0) restartTimer = 1;
Serial.println("App Reset");
}
}
void reconnect() {
static unsigned long lastReconnectAttempt = 0;
unsigned long now = millis();
if (!client.connected()) {
if (now - lastReconnectAttempt > 5000) {
lastReconnectAttempt = now;
Serial.print("Reconectando MQTT...");
String clientId = "ESP32Cancela-";
clientId += String(random(0xffff), HEX);
if (client.connect(clientId.c_str())) {
Serial.println("MQTT Conectado!");
client.subscribe(topic_cmd);
client.publish(topic_status, "Sistema Online");
} else {
Serial.print("falhou, rc=");
Serial.print(client.state());
Serial.println(" Reconectando em 5s");
}
}
}
}
long getDistance(int t, int e) {
digitalWrite(t, LOW); delayMicroseconds(2);
digitalWrite(t, HIGH); delayMicroseconds(10);
digitalWrite(t, LOW);
long duration = pulseIn(e, HIGH, 20000);
if (duration <= 0) return 999;
return duration * 0.034 / 2;
}
void openBarrier(String source) {
if (!barrierOpen) {
digitalWrite(PIN_RED, HIGH);
cancela.write(90);
barrierOpen = true;
lastActionMsg = source;
if (isNightTime()) digitalWrite(RELAY_PIN, HIGH);
lightOffTimer = 0;
delay(150);
digitalWrite(PIN_RED, LOW);
}
}
void closeBarrier(String source) {
if (barrierOpen && !emergencyMode) {
digitalWrite(PIN_RED, HIGH);
cancela.write(0);
barrierOpen = false;
lastActionMsg = source;
lightOffTimer = millis() + 5000;
delay(150);
digitalWrite(PIN_RED, LOW);
}
}
void loop() {
static int lastWifiStatus = -1;
int currentStatus = WiFi.status();
if (currentStatus != lastWifiStatus) {
digitalWrite(PIN_BLUE, (currentStatus == WL_CONNECTED) ? HIGH : LOW);
lastWifiStatus = currentStatus;
}
if (digitalRead(BTN_RESET) == LOW) {
Serial.println("BT Reset pressionado! Reiniciando...");
ESP.restart();
}
if (restartTimer > 0) {
if (millis() - restartTimer > 2000) {
Serial.println("Efetuando restart via App...");
ESP.restart();
}
}
if (Serial.available() > 0) {
String cmd = Serial.readStringUntil('\n');
cmd.trim();
if (cmd == "app_reset") {
lastActionMsg = "App reset";
closeBarrier("App");
restartTimer = millis();
if (restartTimer == 0) restartTimer = 1;
Serial.println("App Reset recebido! Reiniciando em 2s...");
} else if (cmd == "app_emerg_on") {
emergencyMode = true;
lastActionMsg = "App Emerg! On";
digitalWrite(RELAY_PIN, HIGH);
openBarrier("App Emerg");
Serial.println("App Emergency ON!");
} else if (cmd == "app_emerg_off") {
emergencyMode = false;
lastActionMsg = "App Emerg! Off";
closeBarrier("App Normal");
Serial.println("App Emergency OFF!");
}
}
if (lightOffTimer > 0 && millis() > lightOffTimer) {
digitalWrite(RELAY_PIN, LOW);
lightOffTimer = 0;
}
bool currentEmerg = digitalRead(BTN_EMERGENCY);
if (currentEmerg == LOW && lastEmergState == HIGH) {
delay(50); // Debounce
emergencyMode = !emergencyMode;
carAtS2 = false;
if (emergencyMode) {
lastActionMsg = "BT Emerg! On";
if (isNightTime()) digitalWrite(RELAY_PIN, HIGH);
openBarrier("EMERGENCIA");
} else {
lastActionMsg = "BT Emerg! Off";
closeBarrier("Manual");
timerClose = millis();
}
Serial.print("Módulo Emergencia: "); Serial.println(emergencyMode ? "ATIVADO" : "DESATIVADO");
}
lastEmergState = currentEmerg;
if (!emergencyMode && (millis() - lastSensorRead > 200)) {
d1 = getDistance(trig1, echo1);
d2 = getDistance(trig2, echo2);
Serial.print("S1: "); Serial.print(d1);
Serial.print("cm | S2: "); Serial.print(d2); Serial.println("cm");
if (d1 < distThreshold && d1 > 1 && !barrierOpen) {
openBarrier("Entrada");
carAtS2 = false;
timerClose = millis();
}
if (barrierOpen) {
if (d2 < 20 && d2 > 1) {
carAtS2 = true;
timerClose = millis();
lastActionMsg = "S2 Aguarda Fechar";
}
else if (carAtS2 && d2 > 50) {
closeBarrier("Fechar OK");
carAtS2 = false;
}
}
lastSensorRead = millis();
}
if (WiFi.status() == WL_CONNECTED) {
if (!client.connected()) {
reconnect();
}
client.loop();
}
updateDisplay();
}
void updateDisplay() {
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(0, 0);
display.print("Sistema: Ativo");
display.setCursor(0, 15);
display.setTextSize(2);
if (emergencyMode) display.println("EMERGENCIA");
else display.println(barrierOpen ? "ABERTO" : "FECHADO");
display.setTextSize(1);
display.setCursor(0, 40);
display.print("LUZ: ");
display.print(digitalRead(RELAY_PIN) ? "Acesa" : "Apagada");
display.setCursor(0, 55);
display.print("Ultimo: ");
display.print(lastActionMsg);
display.display();
}
/*
README.md
# Sistema de Cancela IoT - Documentação
Este projeto implementa um sistema automatizado de controle de cancela manual e remoto utilizando ESP32, servomotor, sensores ultrassônicos, relé para iluminação e um display OLED.
# Componentes e Conexões
Componente | Pino ESP32 | Descrição
*Servo Motor* | 32 | Controla o braço da cancela (0° Fechado / 90° Aberto);
*Relé (Luz)* | 33 | Aciona a iluminação do sistema;
*Sensor S1 (Entrada)* | 16 (Trig), 13 (Echo) | Detecta a aproximação de um veículo;
*Sensor S2 (Saída)* | 14 (Trig), 27 (Echo) | Detecta a passagem e presença sob a cancela;
*Botão Reset* | 25 | Reinicia o sistema (PULLUP);
*Botão Emergência* | 26 | Alterna o modo de emergência (PULLUP);
*LED Vermelho* | 2 | Status de operação;
*LED Amarelo* | 4 | Alerta/Atenção;
*LED Azul* | 5 | Status do WiFi;
*Display OLED* | I2C (SDA/SCL) | Interface visual do usuário.
# Funcionamento do Sistema
# 1. Fluxo Normal de Operação
A. *Detecção de Entrada*: Quando o sensor *S1* detecta um objeto a menos de 20cm, a cancela abre e a *LUZ* acende;
B. *Passagem*: O sistema monitora o sensor *S2*. Quando o carro chega ao *S2*, o timer de fechamento é pausado por segurança;
C. *Fechamento*: Após o carro sair da área do sensor *S2* (distância > 50cm), a cancela fecha automaticamente;
D. *Temporizador da Luz*: A luz acende apenas entre 19:00 e 06:00 (Horário de Brasília). Ao fechar a cancela, ela permanece acesa por 5 segundos antes de apagar. Se o sistema não conseguir obter o horário (ex: sem internet), a luz permanecerá apagada por padrão.
# 2. Controles Manuais (Botões)
A. *Botão Reset*: Reinicia o sistema imediatamente ao ser pressionado.
B. *Botão Emergência*: Ao ser pressionado, ativa o *Modo Emergência*: a cancela abre imediatamente e a luz acende. O sistema permanece travado nesta posição até que o botão seja pressionado novamente.
# 3. Interface do Painel OLED
O display exibe informações em tempo real:
A. *Sistema*: Estado do sistema (ex: Ativo);
B. *Estado da Cancela*: Exibe `ABERTO`, `FECHADO` ou `EMERGENCIA` em destaque;
C. *Status da Luz*: Indica se a luz está `Acesa` ou `Apagada`;
D. *Último*: Registra a última ação relevante (ex: "BT Reset", "Carro em S2", "App reset").
# 4. Integração com Aplicativo (MQTT / Serial)
O sistema aceita comandos via *MQTT* (através do broker `broker.hivemq.com`) e via *Serial*:
# Comandos MQTT
1. *Tópico de Comando*: `cancela/cmd`
2. *Tópico de Status*: `cancela/status`
3. *Mensagens suportadas*:
A. `abrir`: Abre a cancela e acende a luz;
B. `fechar`: Fecha a cancela e inicia o timer da luz;
C. `emerg`: Alterna o Modo de Emergência (ON/OFF);
D. `reset`: Reinicia o sistema remotamente após 2s.
# Comandos Serial (Simulação)
A. `app_reset`: Realiza o procedimento de reset via software;
B. `app_emerg_on`: Ativa o modo de emergência remotamente;
C. `app_emerg_off`: Desativa o modo de emergência remotamente.
*/