FAQ
TL;DR: On ESP32, over‑synchronisation can waste ~50% CPU; "volatile is not a cure." Use FreeRTOS mutexes/semaphores and plan minimal contention. [Elektroda, PrawieElektronik, post #19782858]
Why it matters: This FAQ helps ESP32 developers share variables safely across two cores while maintaining SD‑card I/O and responsiveness.
Quick Facts
- ESP32 development with ESP‑IDF includes FreeRTOS; tasks, semaphores, and mutexes are native primitives. [Elektroda, khoam, post #19785234]
- Mutexes synchronise access to shared data between threads/cores to prevent races. [Elektroda, khoam, post #19784946]
- Volatile does not provide atomicity or prevent race conditions on shared variables. [Elektroda, PrawieElektronik, post #19782858]
- Over‑synchronisation can waste approximately 50% of a CPU core’s time. [Elektroda, PrawieElektronik, post #19782858]
- For SD I/O, run a higher‑priority task and keep each I/O burst short. [Elektroda, khoam, post #19800415]
How do I safely share variables between ESP32 cores?
Protect each shared variable with a FreeRTOS mutex. Take the lock before reading or writing, and release it immediately after. Keep critical sections short to avoid blocking the other core. This prevents torn reads and inconsistent states. Use this pattern for any data that both cores touch. [Elektroda, khoam, post #19784946]
Is volatile enough for cross‑core variables on ESP32?
No. Volatile only prevents register caching. It does not make access atomic or coordinate readers and writers. You can still get races and inconsistent reads. As one expert put it, “volatile is not a cure.” Use proper synchronisation instead. [Elektroda, PrawieElektronik, post #19782858]
What’s the fastest way to sync shared data in ESP‑IDF?
Reduce how often you need to sync. Process data in blocks and treat completed blocks as immutable. Pass whole blocks between tasks rather than sharing live state. This cuts lock time and contention. Good structure beats clever locking tricks. [Elektroda, JacekCz, post #19801147]
Mutex vs semaphore in FreeRTOS—what should I use?
Use a mutex to protect a resource that only one task should own. Mutexes include priority inheritance to reduce priority inversion. Use a binary semaphore for signaling events rather than ownership. Counting semaphores manage N permits. Keep lock durations short regardless of choice. [Barry, 2016]
How should I structure SD card read/write while keeping the app responsive?
Run SD card I/O in its own high‑priority task. Keep each pass short to minimise blocking. Favour small, quick operations so other tasks can run between calls. Keep other work at lower priority. This preserves throughput and responsiveness. [Elektroda, khoam, post #19800415]
How much CPU can locking cost on ESP32?
It can be significant if used everywhere. One contributor warns synchronisation can burn even 50% of a core. Minimise lock usage and keep critical sections brief. Profile hotspots and refactor shared paths to reduce contention. [Elektroda, PrawieElektronik, post #19782858]
What’s a minimal‑contention pattern for dual‑core sharing?
Partition work into blocks and treat finished blocks as immutable. Hand off block references between tasks instead of editing shared state. This reduces lock time and prevents cores from stepping on each other’s toes. Layer code to keep shared surfaces small. [Elektroda, JacekCz, post #19801147]
Should I use atomic operations, and when?
Consider atomics for simple flags or counters that change independently. For multi‑field updates or coordinated state changes, use a mutex or semaphore. Atomics complement, but do not replace, higher‑level synchronisation. [Elektroda, freebsd, post #19782267]
Any quick steps to add a FreeRTOS mutex in ESP‑IDF?
- Create a mutex once at init (xSemaphoreCreateMutex) and store the handle.
- Before touching shared data, call xSemaphoreTake(mutex, timeout).
- After finishing, call xSemaphoreGive(mutex). [Barry, 2016]
Can I just let one core read while the other writes?
No. Readers can see half‑updated values and inconsistent states. Your concern is valid. “Very good intuition.” Use atomic operations for simple cases, or guard access with a mutex. Define clear ownership so readers never see partial writes. [Elektroda, freebsd, post #19782267]
What about priority inversion when using mutexes?
Priority inversion happens when a low‑priority task holds a lock needed by a high‑priority task. Use FreeRTOS mutexes, which enable priority inheritance to mitigate this. Avoid guarding resources with plain binary semaphores. Keep critical sections short to further reduce risk. [Barry, 2016]
Is Rust worth considering for safer concurrency on ESP32?
Yes, for stronger safety. Rust’s ownership rules prevent many data races at compile time. As one expert said, it’s “worth taking an interest” for concurrent programming. Expect a learning curve and evolving ESP32 support. Start small with FFI or IDF bindings. [Elektroda, freebsd, post #19782267]