#include <DHTesp.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
#include <freertos/semphr.h>
#include <LiquidCrystal_I2C.h>
#define DHT_PIN1 15 // Pino se conectará ao sensor de temperatura 1
#define DHT_PIN2 2 // Pino se conectará ao sensor de temperatura 2
#define LDR_PIN 13 // Pino se conectará ao sensor de luminosidade
#define BUTTON_PIN 12 // Pino se conectará ao botão liga/desliga
#define SISTEMA_ON_LED_PIN 27 // Pino se conectará ao LED de energia do sistema
#define SISTEMA_ON_LED_PIN1 19 // Pino será conectado ao LED de energia da caldeira SENSOR1 CORTINA SOBE
#define SISTEMA_OFF_LED_PIN1 18 // Pino será conectado ao LED de desligamento da caldeira SENSOR1 CORTINA DESCE
#define SISTEMA_ON_LED_PIN2 17 // Pino será conectado ao LED de energia da caldeira SENSOR2 CORTINA SOBE
#define SISTEMA_OFF_LED_PIN2 16 // Pino será conectado ao LED de desligamento da caldeira SENSOR2 CORTINA DESCE
DHTesp dhtSensor; // Objeto para o sensor DHT22
LiquidCrystal_I2C lcd(0x27, 20, 4); // Inicializa o LCD no endereço I2C 0x27
volatile bool systemState = false; // Estado do sistema, inicialmente será desligado (deve ser volátil porque é usado no ISR)
volatile unsigned long lastDebounceTime = 0; // Tempo da última pulsação do botão
const unsigned long debounceDelay = 500; // Tempo de espera para o debouncing (en ms)
// Declaração de filas e semáforos para FreeRTOS
QueueHandle_t temperatureQueue; // Fila para envio de valores de temperatura detectados pelo sensor
QueueHandle_t desiredTemperatureQueue; // Fila para enviar valores de temperatura desejados
SemaphoreHandle_t buttonSemaphore; // Semáforo para gerenciar a mudança de estado do sistema com a interrupção do botão liga/desliga
SemaphoreHandle_t lcdMutex; // Mutex para acesso seguro ao LCD
// Manipuladores de tarefas
TaskHandle_t readSensorTaskHandle = NULL;
TaskHandle_t controlCalderaTaskHandle = NULL;
TaskHandle_t updateLCDTaskHandle = NULL;
// Definimos o símbolo de grau como um caractere personalizado (º)
byte degreeSymbol[8] = {
0b00111,
0b00101,
0b00111,
0b00000,
0b00000,
0b00000,
0b00000,
0b00000
};
// ###INTERRUPÇÕES ###
// Função de interrupção para botão liga/desliga
void IRAM_ATTR handleButtonPress() {
unsigned long currentTime = millis(); // Hora atual
if ((currentTime - lastDebounceTime) > debounceDelay) {
lastDebounceTime = currentTime; // Atualizar última hora de impressão
xSemaphoreGiveFromISR(buttonSemaphore, NULL); // Ativa o botão liga/desliga do semáforo em caso de interrupção, fazendo com que o estado do sistema mude
}
}
// ### SETUP ###
void setup() {
Serial.begin(115200);
dhtSensor.setup(DHT_PIN1, DHTesp::DHT22);
dhtSensor.setup(DHT_PIN2, DHTesp::DHT22);
lcd.init(); // Inicialize o LCD
lcd.backlight();
lcd.createChar(0, degreeSymbol); // Crie o símbolo de grau na posição 0
// Configuração de pinos
pinMode(DHT_PIN1, INPUT_PULLUP); // Configuramos o pino do DHT1 como uma entrada com um resistor pull-up
pinMode(DHT_PIN2, INPUT_PULLUP); // Configuramos o pino do DHT2 como uma entrada com um resistor pull-up
pinMode(BUTTON_PIN, INPUT_PULLUP); // Configuramos o pino do botão como uma entrada com um resistor pull-up
pinMode(LDR_PIN, INPUT_PULLUP); // Configuramos o pino do sensor LDR como uma entrada com um resistor pull-up
pinMode(SISTEMA_ON_LED_PIN, OUTPUT); // Configuramos o pino do LED de alimentação do sistema como saída
pinMode(SISTEMA_ON_LED_PIN1, OUTPUT); // Configuramos o pino do LED de potência da caldeira como saída
pinMode(SISTEMA_OFF_LED_PIN1, OUTPUT); // Configuramos o pino do LED de desligamento da caldeira como saída
pinMode(SISTEMA_ON_LED_PIN2, OUTPUT); // Configuramos o pino do LED de potência da caldeira como saída
pinMode(SISTEMA_OFF_LED_PIN2, OUTPUT); // Configuramos o pino do LED de desligamento da caldeira como saída
// Garantimos que os LEDs comecem apagados
digitalWrite(SISTEMA_ON_LED_PIN, LOW); // Garantimos que o LED do sistema esteja desligado na inicialização
digitalWrite(SISTEMA_ON_LED_PIN1, LOW); // Garantimos que o LED de acendimento da caldeira está apagado no arranque
digitalWrite(SISTEMA_OFF_LED_PIN1, LOW); // Garantimos que o LED da caldeira desligada esteja apagado no arranque
digitalWrite(SISTEMA_ON_LED_PIN2, LOW); // Garantimos que o LED de acendimento da caldeira está apagado no arranque
digitalWrite(SISTEMA_OFF_LED_PIN2, LOW); // Garantimos que o LED da caldeira desligada esteja apagado no arranque
// Defina a interrupção no pino do botão para detectar a borda descendente
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), handleButtonPress, FALLING);
// Inicialize semáforos, mutexes e filas para FreeRTOS
buttonSemaphore = xSemaphoreCreateBinary();
lcdMutex = xSemaphoreCreateMutex();
temperatureQueue = xQueueCreate(10, sizeof(float)); //Fila para envio de temperaturas detectadas (máximo 10 valores)
desiredTemperatureQueue = xQueueCreate(10, sizeof(float)); // Cola para enviar temperaturas deseadas
// Criamos as tarefas nos núcleos ESP32
xTaskCreatePinnedToCore(readSensorTask, "LER SENSOR1", 2048, NULL, 1, &readSensorTaskHandle, 0); // Tarefa para ler o sensor DHT22 (núcleo 0)
xTaskCreatePinnedToCore(readSensorTask, "LER SENSOR2", 2048, NULL, 1, &readSensorTaskHandle, 0); // Tarefa para ler o sensor DHT22 (núcleo 0)
xTaskCreatePinnedToCore(controlCalderaTask, "CONTROLE DE AMBIENCIA", 2048, NULL, 1, &controlCalderaTaskHandle, 1); // Tarefa de controle de ambiencia (núcleo 1)
xTaskCreatePinnedToCore(updateLCDTask, "ATUALIZAR LCD", 2048, NULL, 1, &updateLCDTaskHandle, 1); // Tarefa de atualização do LCD (núcleo 1)
xTaskCreatePinnedToCore(checkLight, "VERIFICACAO DE LUZ", 2048, NULL, 1, NULL, 1); // Tarefa para ler o sensor LDR (núcleo 1)
}
// ### TAREFAS ###
// Tarefa para ler o sensor de temperatura DHT22
void readSensorTask(void *parameter) {
while (true) {
if (systemState) { // Leia os dados apenas se o sistema estiver ligado
TempAndHumidity data = dhtSensor.getTempAndHumidity();
Serial.println("--------");
Serial.println("TEMPERATURA AMBIENTE: " + String(data.temperature, 23) + "ºC"); //TEMEPRATURA DESEJADA
Serial.println("TEMPERATURA AMBIENTE: " + String(data.temperature, 23) + "ºC"); //TEMEPRATURA DESEJADA
Serial.println("HUMIDADE: " + String(data.humidity, 1) + "%");
Serial.println("HUMIDADE: " + String(data.humidity, 1) + "%");
Serial.println("--------");
// Envia a temperatura para a fila para ser processada pela tarefa de controle da caldeira
if (xQueueSend(temperatureQueue, &data.temperature, portMAX_DELAY) != pdPASS) {
Serial.println("Fila cheia. Temperatura não enviada.");
}
}
vTaskDelay(pdMS_TO_TICKS(500)); // Espera 500MS antes da leitura seguinte
}
}
// Tarefa para controlar a ambiencia com base na temperatura detectada e no valor do potenciômetro
void controlCalderaTask(void *parameter) {
float temperature;
float potTemperature = 0;
while (true) {
// Espera receber um valor de temperatura da fila
if (xQueueReceive(temperatureQueue, &temperature, portMAX_DELAY) == pdPASS) {
int potentiometerValue = readPotentiometer(); //Leia o valor do potenciômetro
float newPotTemperature = map(potentiometerValue, 0, 4095, 15, 35); // Mapeamento de potenciômetros na faixa de 15ºC a 35ºC
//Envie a temperatura desejada para a fila somente se ela tiver mudado
if (newPotTemperature != potTemperature) {
potTemperature = newPotTemperature;
xQueueSend(desiredTemperatureQueue, &potTemperature, portMAX_DELAY);
}
Serial.println("Potentiometer set to: " + String(potTemperature) + "ºC");
// Controlamos os LEDs da caldeira consoante a temperatura detectada seja inferior, superior ou igual à desejada.
if (temperature < potTemperature) {
digitalWrite(SISTEMA_ON_LED_PIN1, HIGH);
digitalWrite(SISTEMA_OFF_LED_PIN1, LOW);
digitalWrite(SISTEMA_ON_LED_PIN2, HIGH);
digitalWrite(SISTEMA_OFF_LED_PIN2, LOW);
Serial.println("SISTEMA ATIVADO.");
delay(5000);
digitalWrite(SISTEMA_ON_LED_PIN1, LOW);
digitalWrite(SISTEMA_OFF_LED_PIN1, LOW);
digitalWrite(SISTEMA_ON_LED_PIN2, LOW);
digitalWrite(SISTEMA_OFF_LED_PIN2, LOW);
Serial.println("SISTEMA ATIVADO.");
delay(5000);
} else {
digitalWrite(SISTEMA_ON_LED_PIN1, LOW);
digitalWrite(SISTEMA_OFF_LED_PIN1, HIGH);
digitalWrite(SISTEMA_ON_LED_PIN2, LOW);
digitalWrite(SISTEMA_OFF_LED_PIN2, HIGH);
Serial.println("SISTEMA ATIVADO.");
delay(5000);
digitalWrite(SISTEMA_ON_LED_PIN1, LOW);
digitalWrite(SISTEMA_OFF_LED_PIN1, LOW);
digitalWrite(SISTEMA_ON_LED_PIN2, LOW);
digitalWrite(SISTEMA_OFF_LED_PIN2, LOW);
Serial.println("SISTEMA ATIVADO.");
delay(5000);
}
}
}
}
//Tarefa para atualizar a tela LCD
void updateLCDTask(void *parameter) {
float currentTemperature, desiredTemperature;
while (true) {
// Pega o valor da temperatura atual da fila
if (xQueueReceive(temperatureQueue, ¤tTemperature, portMAX_DELAY) == pdPASS) {
// Se houver um novo valor de temperatura desejado na fila, nós o consideramos
if (xQueueReceive(desiredTemperatureQueue, &desiredTemperature, 0) == pdPASS) {
// Armazenamos o último valor recebido diretamente na cláusula if
}
xSemaphoreTake(lcdMutex, portMAX_DELAY); // Pegue o mutex para acesso exclusivo ao LCD
lcd.clear();
lcd.setCursor(0, 0); //posicao no lcd linha coluna
lcd.print("TEMPERATURA1: " + String(currentTemperature, 1)); //casas depois da virgula
lcd.write(byte(0)); //Insira o símbolo de grau personalizado
lcd.print("C");
lcd.setCursor(0, 1); //posicao no lcd linha coluna
lcd.print("HUMIDADE1: " + String(desiredTemperature, 1)); //casas depois da virgula
lcd.write(byte(0));
lcd.print("C");
xSemaphoreGive(lcdMutex); // Libere o mutex
//REPETICAO TESTE
xSemaphoreTake(lcdMutex, portMAX_DELAY); // Pegue o mutex para acesso exclusivo ao LCD
lcd.setCursor(0, 2); //posicao no lcd linha coluna
lcd.print("TEMPERATURA2: " + String(currentTemperature, 1)); //casas depois da virgula
lcd.write(byte(0)); //Insira o símbolo de grau personalizado
lcd.print("C");
lcd.setCursor(0, 3); //posicao no lcd linha coluna
lcd.print("HUMIDADE2: " + String(desiredTemperature, 1)); //casas depois da virgula
lcd.write(byte(0));
lcd.print("C");
xSemaphoreGive(lcdMutex); // Libere o mutex
}
vTaskDelay(pdMS_TO_TICKS(500)); // Atualiza 1 vez a cada 30 segundos
}
}
// Tarefa para controlar o sensor de luz
void checkLight(void *parameter) {
while (true) {
if (systemState) { // Se o sistema estiver ligado, ele lê o status do LDR, que está configurado como digital
if (digitalRead(LDR_PIN) == HIGH) { // Se não houver luz, suspendemos as tarefas
Serial.println("NAO FOI DETECTADO ENERGIA. SUSPENSAO DAS TAREFAS...");
vTaskSuspend(readSensorTaskHandle);
vTaskSuspend(controlCalderaTaskHandle);
vTaskSuspend(updateLCDTaskHandle);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" NAO HA ENERGIA ");
digitalWrite(SISTEMA_ON_LED_PIN1, LOW);
digitalWrite(SISTEMA_OFF_LED_PIN1, LOW);
} else {
Serial.println("LUZ DETECTADA. RETORNO DAS TAREFAS..."); // Se houver luz, retornamamos as tarefas suspensas
vTaskResume(readSensorTaskHandle);
vTaskResume(controlCalderaTaskHandle);
vTaskResume(updateLCDTaskHandle);
}
}
vTaskDelay(pdMS_TO_TICKS(300)); // Aguarde 300M segundos antes da próxima verificação
}
}
// ### FUNÇÕES ###
// Função para obter o valor atual do potenciômetro
int readPotentiometer() {
if (systemState) {
int value = analogRead(36);
return value;
}
return 0;
}
// Função para alterar o estado do sistema de ligado para desligado e vice-versa
void controlSystem() {
systemState = !systemState;
lcd.clear();
lcd.setCursor(1, 0); //posicao no lcd linha e coluna
lcd.print(systemState ? "SISTEMA LIGADO" : "SISTEMA DESLIGADO");
digitalWrite(SISTEMA_ON_LED_PIN, systemState ? HIGH : LOW);
digitalWrite(SISTEMA_ON_LED_PIN1, LOW);
digitalWrite(SISTEMA_OFF_LED_PIN1, LOW);
Serial.println(systemState ? "SISTEMA LIGADO" : "SISTEMA DESLIGADO");
}
// ### LOOP ###
void loop() {
if (xSemaphoreTake(buttonSemaphore, portMAX_DELAY) == pdTRUE) { //Semáforo para controlar o estado do sistema, ativado quando o botão liga/desliga é pressionado
controlSystem();
}
}