# How to Prevent Race Conditions on Shared Sensor Data in a Real-Time ESP32 System?

**URL:** <https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075>\
**Category:** Programming\
**Created:** [November 24, 2025, 9:25am UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075 "2025-11-24T09:25:14Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![taha\_zarif90](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/taha_zarif90/32/1116577_2.png) [@taha\_zarif90](https://forum.arduino.cc/u/taha_zarif90)\
**Post date:** [November 24, 2025, 9:25am UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/1 "2025-11-24T09:25:15Z")

</div>

In an ESP32-based system where multiple tasks share the same sensor data (IMU, encoders, GPS), what is the correct way to prevent race conditions when one task updates the shared position variables while another task reads them for control logic?  
I’m currently seeing occasional corrupted/half-updated values, and if this isn’t fixed the entire navigation algorithm becomes unstable.  
What’s the proper, deterministic way to protect shared state without breaking real-time performance?

---

<div class="post-metadata">

**Author:** ![sterretje](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/sterretje/32/391294_2.png) [@sterretje](https://forum.arduino.cc/u/sterretje)\
**Post date:** [November 24, 2025, 9:36am UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/2 "2025-11-24T09:36:29Z")

</div>

I'm not an ESP32 user and I don't have experience with RTOS. I think that the term that you're looking for is semaphores.

I hope I do not put you on the wrong foot.

---

<div class="post-metadata">

**Author:** ![taha\_zarif90](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/taha_zarif90/32/1116577_2.png) [@taha\_zarif90](https://forum.arduino.cc/u/taha_zarif90)\
**Post date:** [November 24, 2025, 9:47am UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/3 "2025-11-24T09:47:40Z")

</div>

Thanks yes semaphores are one option but I’m not completely sure they solve the whole problem here The issue I’m seeing looks more like partial-write corruption during reads so I’m trying to figure out whether a mutex critical section, or a double-buffered approach would be more deterministic on the ESP32’s architecture

Still, semaphores might be part of the solution, so I appreciate the pointer

---

<div class="post-metadata">

**Author:** ![Dave\_Lowther](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/dave_lowther/32/1092885_2.png) [@Dave\_Lowther](https://forum.arduino.cc/u/Dave_Lowther)\
**Post date:** [November 24, 2025, 10:04am UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/4 "2025-11-24T10:04:12Z")

</div>

> [@taha\_zarif90](#):
>
> what is the correct way to prevent race conditions when one task updates the shared position variables while another task reads them

Have you looked at [this document](https://github.com/FreeRTOS/FreeRTOS-Kernel-Book/releases/download/V1.1.0/Mastering-the-FreeRTOS-Real-Time-Kernel.v1.1.0.pdf)?

Section 8 covers critical sections, mutexes, and binary semaphores, any of these can provide mutual exclusion to data shared between tasks.

---

<div class="post-metadata">

**Author:** ![gcjr](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/gcjr/32/314015_2.png) [@gcjr](https://forum.arduino.cc/u/gcjr)\
**Post date:** [November 24, 2025, 10:14am UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/5 "2025-11-24T10:14:47Z")

</div>

> [@taha\_zarif90](#):
>
> In an ESP32-based system where multiple tasks share the same sensor data (IMU, encoders, GPS), what is the correct way to prevent race conditions when one task updates the shared position variables while another task reads them for control logi

you're describing sensors that only provide output. There's no reason mutiple "tasks" can't read the sensor at any time. of course the sensor value from one read to the next.

looks like the problem is more about multiple tasks using the I2C bus. Use of the bus needs to be thru a common driver that has mutex/semaphore to suspend 1+ other tasks until the 1st I2C operation completes.

---

<div class="post-metadata">

**Author:** ![6v6gt](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/6v6gt/32/225488_2.png) [@6v6gt](https://forum.arduino.cc/u/6v6gt)\
**Post date:** [November 24, 2025, 10:19am UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/6 "2025-11-24T10:19:24Z")

</div>

It depends on the exact situation but I'd probably consider nominating one task to do the read of the actual sensor, which may be a relatively slow activity, then storing the results in a buffer. This buffer would be protected by a "critical section", implemented by one of the mechanisms already described, and read by the other tasks.

---

<div class="post-metadata">

**Author:** ![gcjr](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/gcjr/32/314015_2.png) [@gcjr](https://forum.arduino.cc/u/gcjr)\
**Post date:** [November 24, 2025, 10:26am UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/7 "2025-11-24T10:26:58Z")

</div>

> [@6v6gt](#):
>
> I'd probably consider nominating one task to do the read of the actual sensor, which may be a relatively slow activity, then storing the results in a buffer.

looks like the i2c bus is used to read multi devices. So either to do as you suggest and have copies of the values from different devices updated periodically or protect the use of the i2c bus

---

<div class="post-metadata">

**Author:** ![taha\_zarif90](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/taha_zarif90/32/1116577_2.png) [@taha\_zarif90](https://forum.arduino.cc/u/taha_zarif90)\
**Post date:** [November 24, 2025, 12:36pm UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/8 "2025-11-24T12:36:23Z")

</div>

Thanks yes I checked section 8  
I’m just trying to decide whether a tiny critical section or a full mutex is the safer choice for these fast-updating position variables  
But your hint helps thanks

---

<div class="post-metadata">

**Author:** ![taha\_zarif90](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/taha_zarif90/32/1116577_2.png) [@taha\_zarif90](https://forum.arduino.cc/u/taha_zarif90)\
**Post date:** [November 24, 2025, 12:38pm UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/9 "2025-11-24T12:38:18Z")

</div>

Good point so I need two protections:  
one mutex around the I2C driver and a separate critical section/mutex for the shared position struct  
I’ll try that setup

---

<div class="post-metadata">

**Author:** ![taha\_zarif90](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/taha_zarif90/32/1116577_2.png) [@taha\_zarif90](https://forum.arduino.cc/u/taha_zarif90)\
**Post date:** [November 24, 2025, 12:41pm UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/10 "2025-11-24T12:41:08Z")

</div>

Thanks that makes sense You're right having a single task handle all sensor reads and then exposing a protected shared buffer is probably the cleanest and safest way I’ll refactor my code in that direction Appreciate the clarification

---

<div class="post-metadata">

**Author:** ![taha\_zarif90](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/taha_zarif90/32/1116577_2.png) [@taha\_zarif90](https://forum.arduino.cc/u/taha_zarif90)\
**Post date:** [November 24, 2025, 12:42pm UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/11 "2025-11-24T12:42:33Z")

</div>

Yes exactly protecting the I2C bus + having one dedicated sensor-reader task feeding a shared buffer is probably the most reliable architecture Thanks for confirming it

---

<div class="post-metadata">

**Author:** ![Dave\_Lowther](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/dave_lowther/32/1092885_2.png) [@Dave\_Lowther](https://forum.arduino.cc/u/Dave_Lowther)\
**Post date:** [November 24, 2025, 1:52pm UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/12 "2025-11-24T13:52:48Z")

</div>

> [@taha\_zarif90](#):
>
> I’m just trying to decide

Others have given good advice. One more suggestion which may or may not fit: Queues can be useful too for indirect mutual exclusion. E.g. if you want to perform I2C operations from muliple tasks it might fit your requirements to have an I2C task doing a blocking read on a queue, then post requests to that queue from other tasks.

---

<div class="post-metadata">

**Author:** ![gfvalvo](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/gfvalvo/32/1013044_2.png) [@gfvalvo](https://forum.arduino.cc/u/gfvalvo)\
**Post date:** [November 24, 2025, 1:58pm UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/13 "2025-11-24T13:58:04Z")

</div>

> [@taha\_zarif90](#):
>
> I’m just trying to decide whether a tiny critical section

No, not a critical section. Those are for freezing out multiple accesses for a very short amount of time (similar to disabling interrupts on a single-tasking Arduino). A critical section will disable interrupts on the subject core of a dual-core ESP32. So no ISRs will run on it and there will be no context switches. It will also cause any code (including ISRs) on the other core to enter a spin lock (thus freezing that core for the duration) if attempts to enter a critical section using the same `portMUX_TYPE` object.

I2C operations would take an eternity compared to the amount of time the code in a critical section should take. And, they may not even be possible with interrupts disabled.

---

<div class="post-metadata">

**Author:** ![taha\_zarif90](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/taha_zarif90/32/1116577_2.png) [@taha\_zarif90](https://forum.arduino.cc/u/taha_zarif90)\
**Post date:** [November 24, 2025, 8:10pm UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/14 "2025-11-24T20:10:51Z")

</div>

That’s a good point using a dedicated I2C task with a request queue is a clean pattern and I’ve used it before when the bottleneck was the bus itself  
In my case the corruption happens on the shared navigation state after the sensors are already read so the contention isn’t on the I2C layer but on the shared structs being updated and consumed at different rates

So yes the queue-based approach definitely helps for serialized I2C access but I probably still need some explicit data-protection (mutex or double-buffering) around the fused state to keep the control loop deterministic

---

<div class="post-metadata">

**Author:** ![taha\_zarif90](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/taha_zarif90/32/1116577_2.png) [@taha\_zarif90](https://forum.arduino.cc/u/taha_zarif90)\
**Post date:** [November 24, 2025, 8:14pm UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/15 "2025-11-24T20:14:00Z")

</div>

You're absolutely right I wouldn’t put I2C inside a critical section either  
I only meant protecting the final shared state (the fused pose struct) not the sensor read itself  
Just a tiny lock or double-buffer there so readers never see half-written data

---

<div class="post-metadata">

**Author:** ![gfvalvo](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/gfvalvo/32/1013044_2.png) [@gfvalvo](https://forum.arduino.cc/u/gfvalvo)\
**Post date:** [November 24, 2025, 8:24pm UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/16 "2025-11-24T20:24:25Z")

</div>

> [@taha\_zarif90](#):
>
> Just a tiny lock or double-buffer there so readers never see half-written data

Perhaps `std::atomic`. See [This Thread](https://forum.arduino.cc/t/thread-safe-data-transfer-interlock-using-std-atomic-and-std-memory-order/1159080).

---

<div class="post-metadata">

**Author:** ![taha\_zarif90](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/taha_zarif90/32/1116577_2.png) [@taha\_zarif90](https://forum.arduino.cc/u/taha_zarif90)\
**Post date:** [November 24, 2025, 8:36pm UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/17 "2025-11-24T20:36:47Z")

</div>

Thanks that thread is super helpful  
I hadn’t considered using `std::atomic` for the fused state itself mostly because I assumed the struct would be too big for atomic ops to matter But after reading the discussion you linked especially PieterP’s explanation about `memory_order_seq_cst` I think it might actually solve the half-update issue without needing locks at all

I’ll experiment with either atomics or a small double-buffered swap and see which one behaves better on the ESP32  
Appreciate the pointer this gives me a clearer direction

---

<div class="post-metadata">

**Author:** ![gfvalvo](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/gfvalvo/32/1013044_2.png) [@gfvalvo](https://forum.arduino.cc/u/gfvalvo)\
**Post date:** [November 24, 2025, 8:49pm UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/18 "2025-11-24T20:49:09Z")

</div>

> [@taha\_zarif90](#):
>
> for the fused state itself

What does "fused state" mean? I really think you only need a mutex. See [Here](https://forum.arduino.cc/t/1-writer-many-readers/1392133/28?_gl=1*1g9fcza*_up*MQ..*_ga*MTk5MTcxNTQyLjE3NjQwMTY5MDk.*_ga_NEXN8H46L5*czE3NjQwMTY5MDkkbzEkZzEkdDE3NjQwMTcwMDckajYwJGwwJGgxMDMyNzE5NTQ0) for a mutex technique that only locks for write access but not read. That allows multiple readers at the same time but only a single writer.

---

<div class="post-metadata">

**Author:** ![taha\_zarif90](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/taha_zarif90/32/1116577_2.png) [@taha\_zarif90](https://forum.arduino.cc/u/taha_zarif90)\
**Post date:** [November 24, 2025, 8:53pm UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/19 "2025-11-24T20:53:11Z")

</div>

Thanks, that example makes sense  
And yeah, by “fused state” I just meant the final navigation state the struct that contains the combined IMU + encoder + GPS estimate

Your shared-mutex pattern looks solid It’s basically what I meant when I said “tiny lock”: multiple readers, one writer, writer priority  
For my case it probably doesn’t need anything more complex than that

I’ll try both approaches (std::atomic for a swap flag, or your shared\_mutex + RAII style) and see which one behaves better on the ESP32 load

Appreciate the detailed example it helps a lot

---

<div class="post-metadata">

**Author:** ![system](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/system/32/1140315_2.png) [@system](https://forum.arduino.cc/u/system)\
**Post date:** [May 23, 2026, 8:53pm UTC](https://forum.arduino.cc/t/how-to-prevent-race-conditions-on-shared-sensor-data-in-a-real-time-esp32-system/1416075/20 "2026-05-23T20:53:32Z")

</div>

Closed on May 23, 2026, 8:53pm UTC

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.
