#include <Arduino.h>
#include <DHT.h>
#include <SPI.h>
#include <SD.h>
#include <RTClib.h>
#include <freertos/semphr.h>
// --- Definições de pinos e tipos ---
#define DHTPIN 4 // Pino de dados do DHT22
#define DHTTYPE DHT22
#define MQ2PIN 34 // Pino analógico do MQ-2 (ADC1_CH6)
#define CSPIN 5 // Pino CS do cartão SD (HSPI)
// Thresholds configuráveis (ajuste conforme seu sensor/calibração):
#define THRESHOLD_TEMP_HIGH 50.0 // Temperatura em °C para risco de incêndio
#define THRESHOLD_SMOKE_HIGH 2000 // Valor analógico para risco de incêndio
#define THRESHOLD_HUM_HIGH 70.0 // Umidade (%) para chuva intensa
// Variáveis compartilhadas (acessadas por múltiplas tasks)
float temperatura = 0.0;
float umidade = 0.0;
int fumaça = 0;
String causa = "Normal";
// Objeto do sensor DHT e do RTC
DHT dht(DHTPIN, DHTTYPE);
RTC_DS1307 rtc;
File logFile;
// Mutex para proteger o acesso às variáveis compartilhadas
SemaphoreHandle_t xSensorMutex;
// Protótipos das tasks
void TaskReadSensors(void *pvParameters);
void TaskEvaluate(void *pvParameters);
void TaskLogSD(void *pvParameters);
void listRootFiles(void* pvParameters);
// Protótipo para listar arquivos no SD
void listRootFiles();
void setup() {
Serial.begin(115200);
delay(500);
Serial.println();
Serial.println("=== Iniciando Sistema de Monitoramento com Semáforos ===");
// Inicializa DHT22
dht.begin();
Serial.println("DHT22 inicializado.");
// Inicializa cartão SD
if (!SD.begin(CSPIN)) {
Serial.println("ERRO: Falha ao inicializar o cartão SD.");
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
Serial.println("Cartão SD inicializado.");
// Inicializa RTC DS1307
if (!rtc.begin()) {
Serial.println("ERRO: Falha ao inicializar o RTC.");
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
if (!rtc.isrunning()) {
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
Serial.println("RTC ajustado para hora de compilação.");
} else {
Serial.println("RTC já está rodando.");
}
// Cria mutex para variáveis de sensores
xSensorMutex = xSemaphoreCreateMutex();
if (xSensorMutex == NULL) {
Serial.println("ERRO: Falha ao criar mutex.");
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
// Criação das tasks
xTaskCreate(
TaskReadSensors, // Função da task
"LeituraSensores", // Nome para debug
4096, // Tamanho da stack
NULL, // Parâmetro (não usado)
1, // Prioridade
NULL // Handle (não usaremos)
);
xTaskCreate(
TaskEvaluate,
"AvaliaCondições",
4096,
NULL,
1,
NULL
);
xTaskCreate(
TaskLogSD,
"LogSD",
8192, // Precisa de mais stack para operações de SD
NULL,
0, // Prioridade menor que as tarefas críticas
NULL
);
// Lista arquivos no diretório raiz do SD
listRootFiles();
}
void loop() {
// Nada no loop; toda a lógica está nas Tasks.
}
/**
* TaskReadSensors:
* - Lê DHT22 (temperatura e umidade) e MQ-2 (fumaça) a cada 2s.
* - Protege acesso às variáveis compartilhadas com mutex.
*/
void TaskReadSensors(void *pvParameters) {
(void) pvParameters;
for (;;) {
float t = dht.readTemperature();
float h = dht.readHumidity();
if (!isnan(t) && !isnan(h)) {
int mq2Value = analogRead(MQ2PIN);
// Protege escrita em "temperatura", "umidade" e "fumaça"
if (xSemaphoreTake(xSensorMutex, pdMS_TO_TICKS(100))) {
temperatura = t;
umidade = h;
fumaça = mq2Value;
xSemaphoreGive(xSensorMutex);
}
Serial.printf("Leituras brutas -> Temp: %.2f °C | Umid: %.2f %% | Fumaça: %d\n",
t, h, mq2Value);
} else {
Serial.println("AVISO: Falha ao ler DHT22. Retentando no próximo ciclo.");
}
vTaskDelay(pdMS_TO_TICKS(2000)); // 2 segundos
}
}
/**
* TaskEvaluate:
* - A cada 1s, acessa as variáveis compartilhadas sob mutex.
* - Avalia: Risco de incêndio (temp > THRESHOLD_TEMP_HIGH || fumaça > THRESHOLD_SMOKE_HIGH)
* ou Chuva intensa (umidade > THRESHOLD_HUM_HIGH)
* ou Normal (nenhuma condição).
* - Atualiza 'causa' sob mutex.
*/
void TaskEvaluate(void *pvParameters) {
(void) pvParameters;
for (;;) {
float t_local;
float h_local;
int f_local;
// Obter cópia local das variáveis
if (xSemaphoreTake(xSensorMutex, pdMS_TO_TICKS(100))) {
t_local = temperatura;
h_local = umidade;
f_local = fumaça;
xSemaphoreGive(xSensorMutex);
} else {
// Se não conseguiu mutex, aguarda um pouco e tenta novamente
vTaskDelay(pdMS_TO_TICKS(500));
continue;
}
// Avalia condições
String novaCausa = "Normal";
if (t_local > THRESHOLD_TEMP_HIGH || f_local > THRESHOLD_SMOKE_HIGH) {
novaCausa = "Risco de incêndio";
} else if (h_local > THRESHOLD_HUM_HIGH) {
novaCausa = "Chuva intensa";
}
// Atualiza variável 'causa'
if (xSemaphoreTake(xSensorMutex, pdMS_TO_TICKS(100))) {
causa = novaCausa;
xSemaphoreGive(xSensorMutex);
}
Serial.printf("Avaliação -> Causa: %s\n", novaCausa.c_str());
vTaskDelay(pdMS_TO_TICKS(1000)); // 1 segundo
}
}
/**
* TaskLogSD:
* - A cada 5s, lê sob mutex as variáveis "temperatura", "umidade", "fumaça" e "causa".
* - Grava em '/datalog.txt' no cartão SD: data/hora, T, statusTemp, U, statusUmid, F, statusFum, causa.
*/
void TaskLogSD(void *pvParameters) {
(void) pvParameters;
// Pequeno delay inicial para estabilizar leituras
vTaskDelay(pdMS_TO_TICKS(3000));
for (;;) {
float t_local;
float h_local;
int f_local;
String causa_local;
// Cópia local das variáveis sob mutex
if (xSemaphoreTake(xSensorMutex, pdMS_TO_TICKS(100))) {
t_local = temperatura;
h_local = umidade;
f_local = fumaça;
causa_local = causa;
xSemaphoreGive(xSensorMutex);
} else {
vTaskDelay(pdMS_TO_TICKS(500));
continue;
}
// Formata data e hora do RTC
DateTime now = rtc.now();
char dateTimeBuf[20];
snprintf(
dateTimeBuf, sizeof(dateTimeBuf),
"%02d/%02d/%04d %02d:%02d:%02d",
now.day(), now.month(), now.year(),
now.hour(), now.minute(), now.second()
);
// Status individuais
String statusTemp = (t_local > THRESHOLD_TEMP_HIGH) ? "Alerta" : "Normal";
String statusFum = (f_local > THRESHOLD_SMOKE_HIGH) ? "Alerta" : "Normal";
String statusHum = (h_local > THRESHOLD_HUM_HIGH) ? "Alerta" : "Normal";
// Abre ou cria o arquivo de log em modo append
logFile = SD.open("/datalog.txt", FILE_APPEND);
if (logFile) {
logFile.printf(
"%s,%.2f,%s,%.2f,%s,%d,%s,%s\n",
dateTimeBuf,
t_local, statusTemp.c_str(),
h_local, statusHum.c_str(),
f_local, statusFum.c_str(),
causa_local.c_str()
);
logFile.close();
// Exibe no Serial para conferência
Serial.printf(
"[LOG] %s | T=%.2f°C (%s) | U=%.2f%% (%s) | F=%d (%s) | Causa=%s\n",
dateTimeBuf,
t_local, statusTemp.c_str(),
h_local, statusHum.c_str(),
f_local, statusFum.c_str(),
causa_local.c_str()
);
} else {
Serial.println("ERRO: Não foi possível abrir /datalog.txt para gravação.");
}
vTaskDelay(pdMS_TO_TICKS(5000)); // 5 segundos
}
}
/**
* listRootFiles:
* - Abre o diretório raiz ("/") no SD e imprime todos os nomes de arquivos/pastas.
*/
void listRootFiles() {
Serial.println("Listando arquivos no diretório raiz do SD:");
File root = SD.open("/");
if (!root) {
Serial.println(" ERRO: não foi possível abrir raiz '/' do SD.");
return;
}
if (!root.isDirectory()) {
Serial.println(" ERRO: '/' não é um diretório.");
root.close();
return;
}
File entry = root.openNextFile();
while (entry) {
Serial.print(" • ");
Serial.println(entry.name());
entry.close();
entry = root.openNextFile();
}
root.close();
}