Módulo 7

Multitasking en ESP32 con FreeRTOS

Sistemas Operativos en Tiempo Real y Multitarea

ESP32 FreeRTOS Multitasking UNAM

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:

C++ - FreeRTOS
// 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

1

LEDs con Diferentes Frecuencias

Principiante 25 min

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

2

Sensor + Comunicación Serial

Intermedio 40 min

Objetivo: Una tarea lee un sensor, otra procesa y envía datos por serial.

Conceptos: Comunicación entre tareas, buffers de datos

3

Control de Servo Multitarea

Avanzado 60 min

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

Referencias y Documentación