Zephyr OS Sensor Fetch and Get: Keep Multiple Consumers on One Sample Generation

Calling sensor_channel_get twice does not request two fresh physical measurements. In Zephyr's fetch-and-get interface, fetch updates driver-held data and get retrieves a channel from that data. When several threads share the device, a fetch by one consumer can change what another consumer reads between its channel requests.

This article uses Zephyr 4.4.0's synchronous sensor interface. It focuses on application-level ownership of a fetched sample, not register-level atomicity or the newer asynchronous sensor decoding path. Those mechanisms have different contracts and should not be assumed interchangeable.

Timeline shows thread A fetching generation one, thread B refreshing generation two between channel gets, and a single-owner snapshot solution.
Original conceptual engineering diagram. Proposed review method, not a measured result.

Understand the two operations

The tagged sensor API defines fetch as obtaining data into an internal driver buffer. Channel get uses that internal data. Repeated gets for the same channel are guaranteed to return the same value when no intervening fetch occurs.

The condition about an intervening fetch is the important part for a shared device. It means that the application must identify all paths that can refresh that device, including a background monitoring thread, a command handler, or a trigger-related worker. Reading only the primary acquisition loop can miss the second producer.

The API also describes synchronous fetch as blocking and warns against calling it from an ISR for I2C- or SPI-connected sensors. An interrupt notification is therefore not a general license to execute the entire fetch sequence inside the interrupt handler.

Model the interleaving explicitly

Consider a hypothetical application that reports temperature and pressure together. Thread A fetches, then gets temperature. Thread B performs another fetch before Thread A gets pressure. The final application record can now combine values associated with different updates of the driver's cache.

This example does not claim that every sensor driver tears individual values or that every project experiences a race. The narrower issue is that separately correct API calls do not automatically form one application transaction. A bus lock around each physical transfer would not necessarily protect the larger fetch-plus-all-required-gets sequence.

Write the sequence on a timeline. Label each successful fetch with a conceptual generation number, then label every get with the generation it can observe. The exercise often exposes the missing ownership rule without needing to argue about scheduler probability.

Choose an application ownership policy

One approach is a single acquisition owner. It performs the fetch, obtains all required channels, and publishes an application-owned snapshot to other consumers. The snapshot can carry a sequence number, validity information, and a clearly defined timestamp. Readers then consume that snapshot instead of independently refreshing the driver cache.

Another approach is to serialize the complete fetch-and-get sequence around the shared device. That only works if every participating path follows the same rule. Locking two gets while leaving an unrelated fetch outside the protection does not establish the intended boundary.

The choice depends on latency, thread structure, and driver behavior. Do not hold a lock across unrelated processing, user-interface rendering, or network transmission merely because the data originated from the sensor. Copy the completed result into application-owned storage and keep the acquisition boundary understandable.

Inspect what the selected driver caches

For a concrete example, the tagged BME280 driver implements separate sample-fetch and channel-get operations. Its getter converts fields from the stored reading into requested channel values. This illustrates why a getter is not itself proof of a new device transaction.

That example should not be generalized into a guarantee that every sensor's channels are sampled simultaneously. Whether physical conversions are simultaneous, sequential, or updated at different rates depends on the device and driver. An application cache generation documents software handling; it does not manufacture physical simultaneity.

If the project uses channel-specific fetches, inspect exactly which cached values that operation updates. A record containing several channels may need validity or age per channel rather than one blanket “fresh” flag.

Handle failure without relabeling old data

Check the fetch result before publishing a new sample generation. If acquisition fails, do not label whatever a later getter returns as a newly successful measurement without a driver-specific basis. Preserve a previous sample only under an explicit stale-data policy, with its prior acquisition identity intact.

Similarly, distinguish acquisition completion time from physical measurement time. If only the former is available, name it accordingly. This article does not propose estimating a sensor's conversion instant from an unrelated task wake time.

A useful verification case deliberately interleaves a second fetch between two channel reads, then repeats it with the chosen ownership policy. Validate the application record's generation and failure behavior. The outcome sought is a coherent software snapshot with honest freshness information, not an unsupported promise that every channel was measured at exactly the same instant.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *