// PROJETO FINAL:
// INDUSTRIAL HEALTH & SAFETY IOT (IHS-IOT):
// SISTEMA DE MONITORAMENTO DE ESTRESSE TÉRMICO E
// DETECÇÃO DE GASES EM AMBIENTES DE PRODUÇÃO INDUSTRIAL
//
// Turma 1 - Embarcados
// ALUNOS: Diego Jaques; Gian Ferrari; Rodolfo Poloni;
//
// ============== Descrição Eventos ==============================================
// Envia (Ubidots):
// Analógicas: temperatura, umidade, gas (ppm)
// Digitais: tempAltaAlarm, umidAltaAlarm, gasAlarm
// Analógicas (limiares): AlarmTempAlta, AlarmUmidAlta, AlarmGas
//
// Recebe (Ubidots /lv):
// AlarmTempAlta, AlarmUmidAlta, AlarmGas
//
// Saídas:
// RELE_EXAUSTOR = tempAltaAlarm || gasAlarm
// RELE_SECADOR = umidAltaAlarm
// LED_VERDE = (!tempAltaAlarm && !umidAltaAlarm && !gasAlarm) || (tempAltaAlarm || umidAltaAlarm)
// LED_VERMELHO = gasAlarm || tempAltaAlarm || umidAltaAlarm
// BUZZER = tempAltaAlarm || umidAltaAlarm || gasAlarm
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <math.h>
//======= WiFi Wokwi ============================================================
const char* WIFI_SSID = "Wokwi-GUEST";
const char* WIFI_PASSWORD = "";
// ====== Ubidots ===============================================================
const char* MQTT_BROKER = "industrial.api.ubidots.com";
const int MQTT_PORT = 1883;
const char* MQTT_CLIENTID = ""; // Wokwi ok
const char* UBIDOTS_TOKEN = "BBUS-WKxSbubJ9nV6Z6UvtYhEKKDVdlAp43";
// Publicação (device)
const char* MQTT_PUB_TOPIC = "/v1.6/devices/esp32proj";
// Tópicos para receber limiares (/lv)
const char* MQTT_SUB_ALARM_TEMP = "/v1.6/devices/esp32proj/alarmtempalta/lv";
const char* MQTT_SUB_ALARM_UMID = "/v1.6/devices/esp32proj/alarmumidalta/lv";
const char* MQTT_SUB_ALARM_GAS = "/v1.6/devices/esp32proj/alarmgas/lv";
// ========= DHT22 =============================================================
#define DHT_PIN 32
#define DHTTYPE DHT22
DHT dht(DHT_PIN, DHTTYPE);
// ======== Pinos I/O ==========================================================
#define GAS_PIN 33 // pino entrada sensor gás MQ2
#define RELE_EXAUSTOR 17 // pino saída relé exaustor
#define RELE_SECADOR 16 // pino saída relé secador de ar
#define LED_VERDE 21 // pino saída acionar LED verde
#define LED_VERMELHO 19 // pino saída acionar LED vermelho
#define BUZZER_PIN 18 // pino saída acionar buzzer
// ======== Limiar defaults Alarmes ===========================================
float AlarmTempAlta = 50.0; // °C
float AlarmUmidAlta = 80.0; // %
float AlarmGas = 400.0; // PPM
// ========= Leituras =========================================================
float temperatura = NAN;
float umidade = NAN;
int gasRaw = 0;
float gasPPM = 0.0;
// ---- Alarmes digitais -------------------------------
bool tempAltaAlarm = false;
bool umidAltaAlarm = false;
bool gasAlarm = false;
char msg[420];
WiFiClient espClient;
PubSubClient mqtt(espClient);
unsigned long lastMsg = 0;
// ===== Calibração sensor MQ2 no Wokwi (GásRAW -> GásPPM) ==========================
// Necessário para a conversão do valor bruto para PPM
static const int rawPts[] = {
868, 1234, 1656, 2345, 3146, 3337, 3497, 3672, 3751,
3821, 3916, 3954, 3990, 4009, 4024, 4036, 4039, 4041
};
static const float ppmPts[] = {
0.1, 0.3, 1.0, 5.0, 50.0, 100.0, 200.0, 525.0, 912.0,
1660.0, 5012.0, 9120.0, 19055.0, 31623.0, 50119.0,
79433.0, 91201.0, 100000.0
};
static const int NPTS = sizeof(rawPts) / sizeof(rawPts[0]);
// Interpolação em log10(ppm) por trechos (mais adequada para curva exponencial)
float rawToPPM_LogInterp(int r) {
if (r <= rawPts[0]) return ppmPts[0];
if (r >= rawPts[NPTS - 1]) return ppmPts[NPTS - 1];
for (int i = 0; i < NPTS - 1; i++) {
if (r >= rawPts[i] && r <= rawPts[i + 1]) {
float x0 = rawPts[i], x1 = rawPts[i + 1];
float y0 = log10f(ppmPts[i]);
float y1 = log10f(ppmPts[i + 1]);
float frac = (r - x0) / (x1 - x0);
return powf(10.0f, y0 + frac * (y1 - y0));
}
}
return ppmPts[NPTS - 1];
}
// Média curta para reduzir jitter (quase sem “lag”)
int readGasRawAvg5() {
long sum = 0;
for (int i = 0; i < 5; i++) {
sum += analogRead(GAS_PIN);
delay(2);
}
return (int)(sum / 5);
}
// ========= Converte payload para float ======================================
static float payloadToFloat(const byte* payload, unsigned int length) {
char buf[32];
unsigned int n = (length < sizeof(buf) - 1) ? length : (sizeof(buf) - 1);
memcpy(buf, payload, n);
buf[n] = '\0';
return atof(buf);
}
void mqttCallback(char* topic, byte* payload, unsigned int length) {
float val = payloadToFloat(payload, length);
if (strcmp(topic, MQTT_SUB_ALARM_TEMP) == 0) {
AlarmTempAlta = val;
Serial.print("[MQTT Sub] AlarmTempAlta [°C] = ");
Serial.println(AlarmTempAlta);
}
else if (strcmp(topic, MQTT_SUB_ALARM_UMID) == 0) {
AlarmUmidAlta = val;
Serial.print("[MQTT Sub] AlarmUmidAlta [%] = ");
Serial.println(AlarmUmidAlta);
}
else if (strcmp(topic, MQTT_SUB_ALARM_GAS) == 0) {
AlarmGas = val;
Serial.print("[MQTT Sub] AlarmGas [PPM] = ");
Serial.println(AlarmGas);
}
}
// ========= Conexão WiFi ======================================================
void connectWiFi() {
Serial.print("[WiFi] Conectando...");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(400);
Serial.print(".");
}
Serial.println("\n[WiFi] Conectado!");
}
// ========= Conexão MQTT ====================================================
void connectMQTT() {
mqtt.setServer(MQTT_BROKER, MQTT_PORT);
mqtt.setCallback(mqttCallback);
while (!mqtt.connected()) {
Serial.println("[MQTT] Conectando...");
if (mqtt.connect(MQTT_CLIENTID, UBIDOTS_TOKEN, "")) {
Serial.println("[MQTT] Conectado!");
mqtt.subscribe(MQTT_SUB_ALARM_TEMP);
mqtt.subscribe(MQTT_SUB_ALARM_UMID);
mqtt.subscribe(MQTT_SUB_ALARM_GAS);
Serial.println("[MQTT] Subscribed: alarmtempalta / alarmumidalta / alarmgas");
} else {
Serial.print("[MQTT] Falhou rc=");
Serial.print(mqtt.state());
Serial.println(" -> tentando novamente em 2s");
delay(2000);
}
}
}
// ======= Acionamento Saídas ==================================================
void updateOutputs() {
// Relés
bool exaustorOn = tempAltaAlarm || gasAlarm;
bool secadorOn = umidAltaAlarm;
digitalWrite(RELE_EXAUSTOR, exaustorOn ? HIGH : LOW);
digitalWrite(RELE_SECADOR, secadorOn ? HIGH : LOW);
// LEDs conforme sua regra (verde pode acender junto com vermelho)
bool ledVerdeOn =
(!tempAltaAlarm && !umidAltaAlarm && !gasAlarm)
|| ((!gasAlarm && tempAltaAlarm) || (!gasAlarm && umidAltaAlarm));
bool ledVermelhoOn =
(gasAlarm || tempAltaAlarm || umidAltaAlarm);
digitalWrite(LED_VERDE, ledVerdeOn ? HIGH : LOW);
digitalWrite(LED_VERMELHO, ledVermelhoOn ? HIGH : LOW);
// Buzzer
bool buzzerOn = (tempAltaAlarm || umidAltaAlarm || gasAlarm);
digitalWrite(BUZZER_PIN, buzzerOn ? HIGH : LOW);
}
// ========== SETUP ============================================================
void setup() {
Serial.begin(115200);
dht.begin(); // inicializa DHT
analogReadResolution(12); // resolução ADC
analogSetPinAttenuation(GAS_PIN, ADC_11db);
pinMode(GAS_PIN, INPUT);
pinMode(RELE_EXAUSTOR, OUTPUT);
pinMode(RELE_SECADOR, OUTPUT);
pinMode(LED_VERDE, OUTPUT);
pinMode(LED_VERMELHO, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
// Inicia com as saídas desligadas
digitalWrite(RELE_EXAUSTOR, LOW);
digitalWrite(RELE_SECADOR, LOW);
digitalWrite(LED_VERDE, LOW);
digitalWrite(LED_VERMELHO, LOW);
digitalWrite(BUZZER_PIN, LOW);
connectWiFi();
connectMQTT();
}
// ========== LOOP ============================================================
void loop() {
if (!mqtt.connected()) connectMQTT();
mqtt.loop();
unsigned long now = millis();
if (now - lastMsg > 2000) {
lastMsg = now;
// Leitura sensor DHT22
float t = dht.readTemperature();
float h = dht.readHumidity();
if (!isnan(t)) temperatura = t;
if (!isnan(h)) umidade = h;
// Leitura sensor de Gás MQ2, calibração e converção (RAW -> PPM)
gasRaw = readGasRawAvg5();
gasPPM = rawToPPM_LogInterp(gasRaw);
// ---- Alarmes digitais (comparando com limiares do Ubidots) ---
tempAltaAlarm = (!isnan(temperatura) && (temperatura > AlarmTempAlta));
umidAltaAlarm = (!isnan(umidade) && (umidade > AlarmUmidAlta));
gasAlarm = (gasPPM > AlarmGas);
// Aciona as saídas
updateOutputs();
// Publica no Ubidots
snprintf(msg, sizeof(msg),
"{"
"\"temperatura\": %.1f, "
"\"umidade\": %.1f, "
"\"gas\": %.1f, "
"\"AlarmTempAlta\": %.1f, "
"\"AlarmUmidAlta\": %.1f, "
"\"AlarmGas\": %.1f, "
"\"tempAltaAlarm\": %d, "
"\"umidAltaAlarm\": %d, "
"\"gasAlarm\": %d"
"}",
isnan(temperatura) ? -999.0 : temperatura,
isnan(umidade) ? -999.0 : umidade,
gasPPM,
AlarmTempAlta,
AlarmUmidAlta,
AlarmGas,
tempAltaAlarm ? 1 : 0,
umidAltaAlarm ? 1 : 0,
gasAlarm ? 1 : 0
);
mqtt.publish(MQTT_PUB_TOPIC, msg);
Serial.println("[MQTT Pub]: ");
Serial.println(msg);
}
}
Exaustor
Secardor Ar
Verde: Normal
Vermelho: Alarme Gás
Amarelo: Alarme Temp/Umidade