Introducción al Multitasking en ESP32
El multitasking o multitarea es una característica fundamental de los sistemas operativos en tiempo real (RTOS) que permite ejecutar múltiples tareas de manera concurrente. En el contexto del ESP32 y FreeRTOS, esta capacidad es esencial para desarrollar aplicaciones robustas en mecatrónica e IoT.
¿Por qué Multitasking en Mecatrónica?
- Sistemas de tiempo real: Respuesta inmediata a eventos críticos
- Eficiencia de recursos: Mejor utilización de ambos núcleos del ESP32
- Modularidad: Separación lógica de funcionalidades
- Escalabilidad: Fácil adición de nuevas funciones
Conceptos Fundamentales de FreeRTOS
FreeRTOS proporciona un conjunto completo de herramientas para implementar multitasking eficiente en el ESP32. Comprende los conceptos clave:
Tareas (Tasks)
Funciones que se ejecutan de forma independiente con su propia pila y estado.
Prioridades
Sistema de niveles que determina qué tarea se ejecuta primero.
Scheduler
Algoritmo que decide qué tarea ejecutar en cada momento.
Núcleos
ESP32 tiene dos núcleos; las tareas pueden asignarse específicamente.
Implementación Básica de Multitasking
Veamos cómo crear tareas básicas en FreeRTOS con ESP32:
// Definir handles para las tareas
TaskHandle_t Task1Handle = NULL;
TaskHandle_t Task2Handle = NULL;
// Tarea 1: Parpadeo LED en núcleo 0
void Task1(void *pvParameters) {
pinMode(LED_BUILTIN, OUTPUT);
for (;;) {
digitalWrite(LED_BUILTIN, HIGH);
Serial.println("Tarea 1: LED Encendido - Núcleo: " + String(xPortGetCoreID()));
vTaskDelay(pdMS_TO_TICKS(1000)); // Delay no bloqueante
digitalWrite(LED_BUILTIN, LOW);
Serial.println("Tarea 1: LED Apagado - Núcleo: " + String(xPortGetCoreID()));
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
// Tarea 2: Monitoreo de sensor en núcleo 1
void Task2(void *pvParameters) {
for (;;) {
int sensorValue = analogRead(A0);
Serial.println("Tarea 2: Sensor = " + String(sensorValue) + " - Núcleo: " + String(xPortGetCoreID()));
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void setup() {
Serial.begin(115200);
// Crear tarea en núcleo 0
xTaskCreatePinnedToCore(
Task1, // Función de la tarea
"LED_Task", // Nombre descriptivo
10000, // Stack size (bytes)
NULL, // Parámetros
1, // Prioridad
&Task1Handle, // Handle de la tarea
0 // Núcleo 0
);
// Crear tarea en núcleo 1
xTaskCreatePinnedToCore(
Task2,
"Sensor_Task",
10000,
NULL,
2, // Mayor prioridad
&Task2Handle,
1 // Núcleo 1
);
Serial.println("Sistema multitarea iniciado");
}
void loop() {
// El loop() se ejecuta en el núcleo 1 con prioridad más baja
Serial.println("Loop principal activo");
delay(3000);
}
Ejercicios Prácticos
Objetivo: Crear dos tareas que controlen LEDs con diferentes frecuencias de parpadeo.
Materiales:
- ESP32 DevKit
- 2 LEDs
- 2 Resistencias 220Ω
- Protoboard y cables
Conexiones: LED1 → Pin 2, LED2 → Pin 4
Objetivo: Una tarea lee un sensor, otra procesa y envía datos por serial.
Conceptos: Comunicación entre tareas, buffers de datos
Objetivo: Control de servomotor basado en lectura de sensor con tareas independientes.
Materiales: ESP32, Servo SG90, Potenciómetro
Proyecto: Estación Meteorológica Multitarea
Sistema de Monitoreo Integral
Desarrolla una estación meteorológica completa utilizando múltiples tareas concurrentes:
Tareas del Sistema:
- Lectura temperatura
- Medición humedad
- Sensor presión
- Transmisión WiFi
Especificaciones:
- Muestreo cada 30s
- Buffer de datos
- Promedios móviles
- Alertas automáticas
Lista de Materiales
- ESP32 DevKit 1
- DHT22 (Temp/Hum) 1
- BMP280 (Presión) 1
- Resistencias 4.7KΩ 2
- Protoboard 1
- Cables Dupont 10
Troubleshooting y Buenas Prácticas
Problemas Comunes
Tareas que no se ejecutan
Verificar prioridades y stack size suficiente
Watchdog Timer Reset
Usar vTaskDelay() en lugar de delay()
Memoria insuficiente
Optimizar stack size de cada tarea
Buenas Prácticas
- Usar nombres descriptivos para tareas
- Implementar manejo de handles correctamente
- Monitorear uso de memoria con uxTaskGetStackHighWaterMark()
- Balancear cargas entre núcleos
- Documentar prioridades asignadas