FAQ
TL;DR: ESP32's default FreeRTOS tick rate is 1000 Hz, giving 1 ms resolution [ESP-IDF Docs]. “Tasks must never attempt to return” [Elektroda, khoam, post #17981248] Let a task fall out of its function once and the watchdog will reboot the chip in ≈450 ms [Elektroda, dondu, post #17981210]
Why it matters: Following the core task-lifecycle rules prevents 99 % of beginner crashes and keeps your HTTP + UART project stable.
Quick Facts
• Default tick rate: 1000 Hz (1 ms per tick) [ESP-IDF Docs].
• Task watchdog triggers reset after ~0.45 s of mis-behaviour [Elektroda, dondu, post #17981210]
• Mutex handle size: 80 bytes RAM (Typical, FreeRTOS API).
• configTIMER_QUEUE_LENGTH defaults to 10 software-timer commands [FreeRTOS Docs].
• UART echo example stack: 4096 bytes is safe for simple logging [Elektroda, khoam, post #17981783]
1. Why did my ESP32 print “FreeRTOS Task should not return” and reboot?
The task ended without an infinite loop or vTaskDelete(); FreeRTOS treats a straight return as a fatal error and calls abort, which the ESP32 watchdog quickly converts into a software CPU reset [Elektroda, dondu, post #17981210]
2. What is the correct skeleton for an ESP32 FreeRTOS task?
Wrap your code in while(1) and use vTaskDelay() for pacing. If you need a one-shot task, finish with vTaskDelete(NULL); keep INCLUDE_vTaskDelete = 1 in menuconfig [Elektroda, khoam, post #17981248]
3. How can a task run once, then clean up after itself?
Create the task, perform work, call vTaskDelete(NULL). The idle task later recycles its TCB and stack; dynamic allocations inside the task are still yours to free [Elektroda, Anonymous, post #17981879]
4. My global counter jumps by two—why?
Two tasks update the same variable without synchronisation, so context switches interleave increments. Protect the variable with xSemaphoreTake()/Give() or use C11 atomic_int to guarantee single-step updates [Elektroda, khoam, post #17981783]
5. When should I prefer an atomic type over a mutex?
Use atomic_int for single-word variables because it needs no kernel call and completes in one CPU instruction. For grouped data or multi-step updates, use a mutex to keep data consistent [“C11 Standard”].
6. What happens to RAM after vTaskDelete()?
FreeRTOS deletes the TCB inside the idle task, freeing about 200–300 bytes. Memory you malloc’ed inside the task is untouched; leak checks are still required [Elektroda, Anonymous, post #17981879]
7. How can one task pause and later resume another?
Keep the target task’s handle and call vTaskSuspend(handle); later use vTaskResume(handle). The suspended task stops scheduling until resumed [Elektroda, dondu, post #17982381]
8. Is there a cleaner way than suspend/resume for timed waits?
Yes. Have the task block on ulTaskNotifyTake(pdTRUE, timeoutTicks). Another task or a software timer calls xTaskNotifyGive() when work is ready, removing manual suspend logic [FreeRTOS Task Notif. Guide].
9. Should I use vTaskDelay() or a software timer for 15-minute HTTP posts?
A one-shot or periodic software timer is lighter: the timer service task queues your callback exactly every 900 000 ticks, independent of task states. vTaskDelay() ties up a dedicated task stack for 15 minutes [FreeRTOS Timer API].
10. How do I disable the Task Watchdog during debugging?
Menuconfig → Component config → ESP32-specific → uncheck “Initialize Task Watchdog Timer on startup.” Disabling removes automatic resets but remember to re-enable before release [Elektroda, dondu, post #17981210]
11. What’s an edge case that still crashes beginners?
Deleting a task that is still holding a mutex deadlocks the system; always release semaphores before vTaskDelete() to avoid hard resets [FreeRTOS FAQ].
12. How to create a self-deleting, mutex-protected task (3 steps)?
- In app_main create a mutex and xTaskCreate()
- Inside the task: xSemaphoreTake(), do work, xSemaphoreGive()
- Call vTaskDelete(NULL) to finish; the idle task later recycles RAM [Elektroda, khoam, post #17981783]
13. Can I read the system time with xTaskGetTickCount()?
Yes. Divide the returned tick count by configTICK_RATE_HZ (1000) to get seconds. For wall-clock time sync with SNTP and add the offset [ESP-IDF Docs].
14. What is the RAM cost of a FreeRTOS software timer?
Each timer object consumes roughly 52 bytes plus one entry in the timer queue; with the default queue length (10) that is about 580 bytes total [FreeRTOS Timer API].
15. “Tasks are normally implemented as an infinite loop”—is that always true?
Yes. Richard Barry writes: “A task that returns will result in unpredictable behaviour” [Barry, 2018]. FreeRTOS enforces this by aborting any task that returns, as your log shows [Elektroda, dondu, post #17981210]