#include <stdbool.h>
#include <Arduino.h>
#include <stdint.h>
#include "DHTesp.h"
#include "freertos/timers.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "Constantes.h"
typedef enum {
ESTADO_DETENIDO_MANUAL,
ESTADO_ABRIENDO_MANUAL,
ESTADO_CERRANDO_MANUAL,
ESTADO_BLOQUEADO_MANUAL,
ESTADO_DETENIDO_AUTO,
ESTADO_ABRIENDO_AUTO,
ESTADO_CERRANDO_AUTO,
ESTADO_BLOQUEADO_AUTO
} estado_t;
typedef enum {
EVT_ABRIR_M,
EVT_CERRAR_M,
EV_CHANGE_MODE_AUTO_SENSOR,
EV_CHANGE_MODE_MANUAL,
EVT_HUMEDAD,
EVT_LLUVIA,
EVT_PIR_ON,
EVT_TIMEOUT_PIR,
EVT_FC_ABIERTO, // 1) en el diagrama
EVT_FC_CERRADO, // 2) en el diagrama
EVT_EMRGENCE, // 3) en el diagrama
EVT_CONTINUE,
} tipo_evento_t;
// Definicion de variables globales
estado_t estado_actual = ESTADO_DETENIDO_MANUAL;
TimerHandle_t pirTimer;
short indice_sensor = INDICE_SENSOR;
bool lluvia = false;
bool humedad = false;
QueueHandle_t queueEvents;
TaskHandle_t loopTaskHandler;
TaskHandle_t loopNewEventHandler;
TaskHandle_t handler_timeout;
DHTesp dht;
//Funciones de hard y sensores
int leer_humedad() {
TempAndHumidity data = dht.getTempAndHumidity();
Serial.printf("humedad %.1f %%\n", data.humidity);
return data.humidity;
}
int leer_lluvia(){
return analogRead(GPIO_Lluvia);
}
int leer_pir(){
return digitalRead(GPIO_PIR);
}
void encender_led(){
digitalWrite(GPIO_LED_IND,HIGH);
Serial.printf("[Hardware] LED Indicador ENCENDIDO");
}
void apagar_led(){
digitalWrite(GPIO_LED_IND,LOW);
}
void motor_control(int vel, int d1, int d2)
{
Serial.printf("[Motor] Velocidad: %d | Dir1: %d | Dir2: %d\n", vel, d1, d2);
// Genera el pulso PWM en el pin determinado
// vel: 0 = 0V (LOW), 255 = 3.3V (HIGH)
analogWrite(motor1PWM, vel);
digitalWrite(motor1Dir1 , d1);
digitalWrite(motor1Dir2 , d2);
}
bool boton_emergencia_presionado() {
return digitalRead(GPIO_EMERG) == LOW;
}
//Verificaciones de sensores
tipo_evento_t verificarEmergencia() {
static int ultimo_estado = HIGH;
int estado_actual = digitalRead(GPIO_EMERG);
if (estado_actual == LOW && ultimo_estado == HIGH) {
Serial.printf("[Sensor] Emergencia detectada.\n");
encender_led();
ultimo_estado = estado_actual;
return EVT_EMRGENCE;
}
else if (estado_actual == HIGH && ultimo_estado == LOW) {
apagar_led();
}
ultimo_estado = estado_actual;
return EVT_CONTINUE;
}
tipo_evento_t verificarTimeout() {
if (ulTaskNotifyTake(pdTRUE, NO_WAIT) > NO_NOTIF) {
Serial.printf("[Sensor] Timeout de PIR completado.");
return EVT_TIMEOUT_PIR;
}
return EVT_CONTINUE;
}
tipo_evento_t verificarLluvia() {
if (estado_actual == ESTADO_DETENIDO_MANUAL ||
estado_actual == ESTADO_ABRIENDO_MANUAL ||
estado_actual == ESTADO_CERRANDO_MANUAL ||
estado_actual == ESTADO_BLOQUEADO_MANUAL ||
lluvia == true) {
return EVT_CONTINUE;
}
if (leer_lluvia() > LLUVIA_MIN_VALIDA && lluvia != true) {
Serial.printf("[Sensor] Lluvia detectada.");
lluvia=true;
return EVT_LLUVIA;
}
else if (leer_lluvia() <= LLUVIA_MIN_VALIDA){
lluvia=false;
}
return EVT_CONTINUE;
}
tipo_evento_t verificarHumedad() {
if (estado_actual == ESTADO_DETENIDO_MANUAL ||
estado_actual == ESTADO_ABRIENDO_MANUAL ||
estado_actual == ESTADO_CERRANDO_MANUAL ||
estado_actual == ESTADO_BLOQUEADO_MANUAL ||
lluvia == true
) {
return EVT_CONTINUE;
}
static uint32_t ultima_lectura = RESET_LECTURA;
if (millis() - ultima_lectura >= INTERVALO) {
ultima_lectura = millis();
int humedad_actual = leer_humedad();
if (humedad_actual > HUMEDAD_UMBRAL_MAX && humedad == false) {
Serial.printf("[Sensor] Humedad alta detectada.");
humedad = true;
return EVT_HUMEDAD;
}
else if (humedad_actual < HUMEDAD_UMBRAL_MAX && humedad == true) {
Serial.printf("humedad %d\n", humedad_actual);
Serial.printf("[Sensor] Humedad baja detectada.");
humedad = false;
}
}
return EVT_CONTINUE;
}
tipo_evento_t verificarPIR() {
if (leer_pir() == HIGH) {
Serial.printf("[Sensor] Movimiento detectado por PIR.");
xTimerReset(pirTimer, NO_WAIT); // Reinicia el timer automáticamente
return EVT_PIR_ON;
}
return EVT_CONTINUE;
}
tipo_evento_t verificarFC_Abierto() {
static int ultimo_estado = LOW;
int estado_actual = digitalRead(GPIO_FC_A);
if (estado_actual == HIGH && ultimo_estado == LOW) {
Serial.printf("[Sensor] Final de carrera APERTURA presionado.\n");
ultimo_estado = estado_actual;
return EVT_FC_ABIERTO;
}
ultimo_estado = estado_actual;
return EVT_CONTINUE;
}
tipo_evento_t verificarFC_Cerrado() {
static int ultimo_estado= LOW;
int estado_actual = digitalRead(GPIO_FC_C);
if (estado_actual == HIGH && ultimo_estado == LOW) {
Serial.printf("[Sensor] Final de carrera CIERRE presionado.\n");
ultimo_estado = estado_actual;
return EVT_FC_CERRADO;
}
ultimo_estado= estado_actual;
return EVT_CONTINUE;
}
tipo_evento_t (*verificar_sensor[MAX_EVENTOS])(void) = {
verificarEmergencia,
verificarTimeout,
verificarPIR,
verificarFC_Abierto,
verificarFC_Cerrado,
verificarLluvia,
verificarHumedad,
verificarComandosAndroid
};
tipo_evento_t verificarComandosAndroid() {
if (!Serial.available()) {
return EVT_CONTINUE;
}
char c = tolower(Serial.read());
switch (c) {
case 'a': return EVT_ABRIR_M;
case 'c': return EVT_CERRAR_M;
case 'm': return EV_CHANGE_MODE_MANUAL;
case 's': return EV_CHANGE_MODE_AUTO_SENSOR;
default: return EVT_CONTINUE;
}
}
tipo_evento_t get_event() {
tipo_evento_t ev_recibido;
if (xQueueReceive(queueEvents, &ev_recibido, portMAX_DELAY)) {
return ev_recibido;
}
return EVT_CONTINUE;
}
void fsm() {
tipo_evento_t input = get_event();
estado_t estado_anterior = estado_actual;
Serial.printf("[FSM] Procesando evento %d en el estado %d\n", input, estado_actual);
switch (estado_actual) {
// --- RAMA MANUAL ---
case ESTADO_DETENIDO_MANUAL:
switch (input) {
case EVT_ABRIR_M:
Serial.printf("COMANDO ABRIR MANUAL");
estado_actual = ESTADO_ABRIENDO_MANUAL;
motor_control(VELOCIDAD, HIGH, LOW);
break;
case EVT_CERRAR_M:
estado_actual = ESTADO_CERRANDO_MANUAL;
motor_control(VELOCIDAD, LOW, HIGH);
break;
case EV_CHANGE_MODE_AUTO_SENSOR:
estado_actual = ESTADO_DETENIDO_AUTO;
break;
default: break;
}
break;
case ESTADO_ABRIENDO_MANUAL:
switch (input) {
case EVT_FC_ABIERTO:
case EVT_EMRGENCE:
estado_actual = ESTADO_DETENIDO_MANUAL;
motor_control(STOP, LOW, LOW);
break;
case EVT_CERRAR_M:
estado_actual = ESTADO_CERRANDO_MANUAL;
motor_control(VELOCIDAD, LOW, HIGH);
break;
default: break;
}
break;
case ESTADO_CERRANDO_MANUAL:
switch (input) {
case EVT_FC_CERRADO:
case EVT_EMRGENCE:
estado_actual = ESTADO_DETENIDO_MANUAL;
motor_control(STOP, LOW, LOW);
break;
case EVT_ABRIR_M:
estado_actual = ESTADO_ABRIENDO_MANUAL;
motor_control(VELOCIDAD, HIGH, LOW);
break;
case EVT_PIR_ON:
estado_actual = ESTADO_BLOQUEADO_MANUAL;
motor_control(STOP, LOW, LOW);
break;
default: break;
}
break;
case ESTADO_BLOQUEADO_MANUAL:
switch (input) {
case EVT_TIMEOUT_PIR:
estado_actual = ESTADO_CERRANDO_MANUAL;
motor_control(VELOCIDAD, LOW, HIGH);
break;
case EVT_PIR_ON:
// Se mantiene bloqueado (El timer ya se reinició en la verificación)
break;
default: break;
}
break;
// --- RAMA AUTOMÁTICA ---
case ESTADO_DETENIDO_AUTO:
switch (input) {
case EVT_LLUVIA:
Serial.printf("Evento lluvia \n ");
estado_actual = ESTADO_CERRANDO_AUTO;
motor_control(VELOCIDAD, LOW, HIGH);
break;
case EVT_HUMEDAD:
Serial.printf("Evento humedad \n ");
estado_actual = ESTADO_ABRIENDO_AUTO;
motor_control(VELOCIDAD, HIGH, LOW);
break;
case EV_CHANGE_MODE_MANUAL:
estado_actual = ESTADO_DETENIDO_MANUAL;
break;
default: break;
}
break;
case ESTADO_ABRIENDO_AUTO:
switch (input) {
case EVT_FC_ABIERTO:
case EVT_EMRGENCE:
Serial.printf("Activando emergencia \n ");
estado_actual = ESTADO_DETENIDO_AUTO;
motor_control(STOP, LOW, LOW);
break;
case EVT_LLUVIA:
Serial.printf("Evento lluvia \n ");
estado_actual = ESTADO_CERRANDO_AUTO;
motor_control(VELOCIDAD, LOW, HIGH);
break;
default: break;
}
break;
case ESTADO_CERRANDO_AUTO:
switch (input) {
case EVT_FC_CERRADO:
case EVT_EMRGENCE:
estado_actual= ESTADO_DETENIDO_AUTO;
motor_control(STOP, LOW, LOW);
break;
case EVT_HUMEDAD:
estado_actual = ESTADO_ABRIENDO_AUTO;
motor_control(VELOCIDAD, HIGH, LOW);
break;
case EVT_PIR_ON:
estado_actual= ESTADO_BLOQUEADO_AUTO;
motor_control(STOP, LOW, LOW);
break;
default: break;
}
break;
case ESTADO_BLOQUEADO_AUTO:
switch (input) {
case EVT_TIMEOUT_PIR:
estado_actual = ESTADO_CERRANDO_AUTO;
motor_control(VELOCIDAD, LOW, HIGH);
break;
case EVT_PIR_ON:
// Se mantiene bloqueado
break;
default: break;
}
break;
}
// Validamos si hubo cambio de estado para informarlo por consola
if (estado_actual != estado_anterior) {
Serial.printf("[FSM] -> Cambio de Estado: %d al %d\n", estado_anterior, estado_actual);
}
}
// Tareas FreeRTOS
void vFSMTask(void *pvParameters) {
Serial.printf("[Sistema] Tarea FSM Iniciada.\n");
while (1) {
fsm();
}
}
void vGetEventTask(void *pvParameters) {
tipo_evento_t ev;
Serial.printf("[Sistema] Tarea GetEvent Iniciada.\n");
while (1) {
ev = verificar_sensor[indice_sensor]();
if (ev != EVT_CONTINUE) {
xQueueSend(queueEvents, &ev, NO_WAIT);
}
indice_sensor++;
if (indice_sensor >= MAX_EVENTOS) {
indice_sensor = INDICE_SENSOR; // Reiniciamos el índice al primer sensor
// Recién acá, habiendo revisado TODOS, hacemos la pausa de 20ms
vTaskDelay(pdMS_TO_TICKS(INTERVALO_TAREAS));
}
}
}
// Modelar timeout
void pirTimeoutCallback(TimerHandle_t xTimer) {
tipo_evento_t evt = EVT_TIMEOUT_PIR;
xQueueSend(queueEvents, &evt, NO_WAIT);
Serial.printf("[Timer] Callback de PIR disparado. Evento enviado.\n");
}
void callbackPIR(TimerHandle_t xTimer) {
xTaskNotifyGive(handler_timeout);
}
/////////////////////////////////////////////
void inicializacion(){
Serial.begin(115200);
Serial.printf("\n--- Iniciando Sistema ---\n");
// Entradas
pinMode(GPIO_Lluvia, INPUT);
pinMode(GPIO_DHT11, INPUT);
//
pinMode(GPIO_FC_A, INPUT_PULLDOWN);
pinMode(GPIO_FC_C, INPUT_PULLDOWN);
pinMode(GPIO_EMERG, INPUT_PULLUP);
//
pinMode(GPIO_PIR, INPUT);
// Salidas
pinMode(GPIO_LED_IND, OUTPUT);
// Configurar PWM del motor
ledcAttach(motor1PWM, PWM_FREQ, PWM_RES);
pinMode(motor1Dir1, OUTPUT);
pinMode(motor1Dir2, OUTPUT);
//DHT
dht.setup(GPIO_DHT11, DHTesp::DHT22);
estado_actual = ESTADO_DETENIDO_MANUAL;
pirTimer = xTimerCreate(
"PIR Timer",
pdMS_TO_TICKS(PIR_TIME),
pdFALSE,
NULL,
pirTimeoutCallback
);
queueEvents = xQueueCreate(QUEUE_SIZE, sizeof(tipo_evento_t));
xTaskCreate(vFSMTask, "LoopFSM", STACK_SIZE, NULL, PRIORIDAD_TAREAS, &loopTaskHandler);
xTaskCreate(vGetEventTask, "EventTask", STACK_SIZE, NULL, PRIORIDAD_TAREAS, &loopNewEventHandler);
}
void setup(){
inicializacion();
}
void loop(){
// Se implementa en freeRtos
}
L298 con 1 solo motor:
Rojo = ENABLED
Azul = INPUT 1
Amariilo = INPUT 2
Pulsador negro: Final de carrera apertura ventana
Pulsador azul: Final de carrera cierre ventana
Pulsador rojo: pulsador de emergencia
4to led rojo: Led indicador
Sensor de lluvia
9v para Puente H
5v - Sensor PIR