# Example 30: _TASK_THREAD_SAFE - Multi-threaded TaskScheduler ## Overview This example demonstrates the use of the `_TASK_THREAD_SAFE` compile option for safe multi-threaded operation with TaskScheduler on FreeRTOS-based platforms (ESP32, ESP8266, STM32, etc.). ## What it Demonstrates ### 1. Thread-Safe Request Queue - Implementation of required `_task_enqueue_request()` and `_task_dequeue_request()` functions - FreeRTOS queue for handling task control requests from multiple threads - Automatic handling of ISR vs. normal task context ### 2. ISR-Safe Operations - Button interrupt triggering StatusRequest signals - Hardware timer ISR changing task parameters - Proper use of `requestAction()` from interrupt context ### 3. Multi-Thread Task Control - FreeRTOS worker thread adjusting task parameters - Safe task manipulation from non-scheduler threads - Queue-based communication between threads ### 4. StatusRequest Integration - Task waiting on StatusRequest completion - Signaling from ISR context - Automatic task activation on event ## Hardware Requirements - **ESP32** development board (recommended) - Built-in LED (LED_BUILTIN) - Boot button (GPIO 0 on most boards) Also compatible with: - ESP8266 - STM32 with FreeRTOS - Any Arduino-compatible board with FreeRTOS support ## Expected Behavior 1. **LED Blinking**: LED blinks at varying rates (controlled by worker thread and timer ISR) 2. **Button Press**: Pressing the boot button triggers a fast blink burst 3. **Timer Events**: Every 7 seconds, hardware timer toggles blink rate 4. **Thread Updates**: Worker thread changes blink interval every 3 seconds 5. **Statistics**: Serial output shows counters and current state every 5 seconds ## Key Components ### Queue Implementation ```cpp // Required global queue QueueHandle_t tsQueue; uint8_t tsQueueData[TS_QUEUE_LEN * sizeof(_task_request_t)]; StaticQueue_t tsQueueBuffer; // Required enqueue function bool _task_enqueue_request(_task_request_t* req) { if (xPortInIsrContext()) { // ISR context BaseType_t xHigherPriorityTaskWokenByPost = pdFALSE; BaseType_t rc = xQueueSendFromISR(tsQueue, req, &xHigherPriorityTaskWokenByPost); if (xHigherPriorityTaskWokenByPost) portYIELD_FROM_ISR(); return (rc == pdTRUE); } else { // Normal task context return (xQueueSend(tsQueue, req, pdMS_TO_TICKS(TS_ENQUEUE_WAIT_MS)) == pdTRUE); } } // Required dequeue function bool _task_dequeue_request(_task_request_t* req) { return (xQueueReceive(tsQueue, req, TS_DEQUEUE_WAIT_MS) == pdTRUE); } ``` ### Safe Task Control from ISR ```cpp void IRAM_ATTR buttonISR() { // Signal StatusRequest from ISR (thread-safe) ts.requestAction(&buttonPressed, TASK_SR_REQUEST_SIGNALCOMPLETE, 0, 0, 0, 0, 0); isrTriggers++; } void IRAM_ATTR timerISR() { // Change task interval from ISR (thread-safe) ts.requestAction(&tBlink, TASK_REQUEST_SETINTERVAL, newInterval, 0, 0, 0, 0); } ``` ### Safe Task Control from Worker Thread ```cpp void workerThread(void* parameter) { while (true) { vTaskDelay(pdMS_TO_TICKS(3000)); // Safely adjust blink interval from worker thread uint32_t newInterval = 300 + (counter % 5) * 100; ts.requestAction(&tBlink, TASK_REQUEST_SETINTERVAL, newInterval, 0, 0, 0, 0); } } ``` ## Building and Running ### PlatformIO ```bash # Navigate to example directory cd lib/TaskScheduler/examples/Scheduler_example30_THREAD_SAFE # Build and upload pio run -t upload # Monitor serial output pio device monitor ``` ### Arduino IDE 1. Open `src/main.cpp` as a `.ino` file 2. Install TaskScheduler library (version 3.8.0 or newer) 3. Select ESP32 board 4. Add to Arduino IDE preprocessor defines: ``` -D_TASK_THREAD_SAFE -D_TASK_STATUS_REQUEST -D_TASK_TIMEOUT -D_TASK_ISR_SUPPORT ``` 5. Compile and upload ## Serial Output Example ``` =========================================== TaskScheduler Example 30: _TASK_THREAD_SAFE =========================================== Task request queue created (size: 16) Button interrupt attached (press boot button) Hardware timer started (triggers every 7 seconds) Worker thread created --- System initialized --- Expected behavior: 1. LED blinks at variable rate 2. Press button for fast blink burst 3. Timer ISR changes rate every 7s 4. Worker thread adjusts rate every 3s 5. All operations are thread-safe! [5123] Blink #10 (ISR triggers: 2, Thread updates: 1) Button pressed! Starting fast blink sequence... === Statistics at 10000 ms === Total blinks: 45 ISR triggers: 2 Thread updates: 3 Current blink interval: 400 ms ================================ ``` ## Configuration Adjust these constants in `main.cpp`: ```cpp #define TS_QUEUE_LEN 16 // Request queue size (increase if overflow occurs) #define TS_ENQUEUE_WAIT_MS 10 // Max wait for enqueue (ms) #define TS_DEQUEUE_WAIT_MS 0 // No wait for dequeue ``` ## Troubleshooting ### Queue Overflow If you see "Failed to enqueue request" messages: - Increase `TS_QUEUE_LEN` - Increase `TS_ENQUEUE_WAIT_MS` - Reduce request frequency ### Watchdog Timeouts If the ESP32 reboots with watchdog errors: - Ensure `ts.execute()` runs frequently in loop() - Check that no task blocks for too long - Reduce worker thread priority ### Compilation Errors Ensure all compile flags are set in `platformio.ini`: - `_TASK_THREAD_SAFE` (required) - `_TASK_STATUS_REQUEST` (for button example) - `_TASK_ISR_SUPPORT` (for ESP ISR methods) ## Learning Points ### ✅ DO: - Always use `requestAction()` from ISRs and worker threads - Implement both enqueue and dequeue functions - Size the queue based on request frequency - Mark ISR functions with `IRAM_ATTR` (ESP32/ESP8266) ### ❌ DON'T: - Call task methods directly from ISRs or other threads - Skip queue implementation (required for _TASK_THREAD_SAFE) - Forget to process requests (automatically done in `execute()`) - Block in ISRs waiting for queue space ## Related Examples - **Example 27**: PlatformIO project structure - **Example 28**: Tickless operation with FreeRTOS - **Example 04/05**: StatusRequest basics ## Further Reading - [TaskScheduler Wiki](https://github.com/arkhipenko/TaskScheduler/wiki) - TaskScheduler_THREAD_SAFE.md in your project root - FreeRTOS queue documentation - ESP32 interrupt handling ## License Same as TaskScheduler library (see library LICENSE file) ## Version Created for TaskScheduler v3.8.0+ Updated for TaskScheduler v4.0.0 (October 2024)