ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
freertos_queue.h
Go to the documentation of this file.
1#pragma once
2
4
5#ifdef ESPHOME_THREAD_MULTI_NO_ATOMICS
6
7#include <cstddef>
8#include <cstdint>
9
10#include <FreeRTOS.h>
11#include <queue.h>
12
13/*
14 * FreeRTOS queue wrapper for single-producer single-consumer scenarios on
15 * platforms without hardware atomic support (e.g. BK72xx ARM968E-S).
16 *
17 * Provides the same API as LockFreeQueue (push, pop, get_and_reset_dropped_count,
18 * empty, full, size) but uses xQueue internally, which synchronizes via
19 * FreeRTOS critical sections. Uses xQueueCreateStatic so the queue storage
20 * lives in BSS with zero runtime heap allocation.
21 *
22 * @tparam T The type of elements stored in the queue (stored as pointers)
23 * @tparam SIZE The maximum number of elements
24 */
25
26namespace esphome {
27
28template<class T, uint8_t SIZE> class FreeRTOSQueue {
29 public:
30 FreeRTOSQueue() : dropped_count_(0) { xQueueCreateStatic(SIZE, sizeof(T *), this->storage_, &this->queue_buf_); }
31
32 // No destructor — ESPHome components are never destroyed. Intentionally
33 // omitted to avoid pulling in vQueueDelete code on resource-constrained targets.
34
35 // Non-copyable, non-movable — queue handle is not transferable
36 FreeRTOSQueue(const FreeRTOSQueue &) = delete;
40
41 bool push(T *element) {
42 if (element == nullptr)
43 return false;
44
45 if (xQueueSend(this->handle_(), &element, 0) != pdPASS) {
47 return false;
48 }
49 return true;
50 }
51
52 T *pop() {
53 T *element;
54 if (xQueueReceive(this->handle_(), &element, 0) != pdTRUE) {
55 return nullptr;
56 }
57 return element;
58 }
59
61 // Fast path: plain read of aligned uint16_t is a single ARM load instruction.
62 // Worst case is reading a stale zero and reporting drops one iteration later.
63 // Avoids critical section overhead on every loop() call since drops are rare.
64 if (this->dropped_count_ == 0)
65 return 0;
66 // Declare outside critical section — BK72xx portENTER_CRITICAL may introduce a scope
67 uint16_t count;
68 portENTER_CRITICAL();
69 count = this->dropped_count_;
70 this->dropped_count_ = 0;
71 portEXIT_CRITICAL();
72 return count;
73 }
74
76 portENTER_CRITICAL();
77 this->dropped_count_++;
78 portEXIT_CRITICAL();
79 }
80
81 bool empty() const { return uxQueueMessagesWaiting(this->handle_()) == 0; }
82
83 bool full() const { return uxQueueSpacesAvailable(this->handle_()) == 0; }
84
85 size_t size() const { return uxQueueMessagesWaiting(this->handle_()); }
86
87 protected:
88 // Static storage for the queue — lives in BSS, no heap allocation
89 uint8_t storage_[SIZE * sizeof(T *)];
90 StaticQueue_t queue_buf_;
92
93 // xQueueCreateStatic() returns the static queue structure itself as the handle;
94 // its only other checks are asserts, and a zero length is the one we could hit
95 static_assert(SIZE > 0, "FreeRTOSQueue needs at least one slot");
96 QueueHandle_t handle_() const {
97 return reinterpret_cast<QueueHandle_t>(const_cast<StaticQueue_t *>(&this->queue_buf_));
98 }
99};
100
101} // namespace esphome
102
103#endif // ESPHOME_THREAD_MULTI_NO_ATOMICS
QueueHandle_t handle_() const
FreeRTOSQueue(FreeRTOSQueue &&)=delete
uint8_t storage_[SIZE *sizeof(T *)]
FreeRTOSQueue(const FreeRTOSQueue &)=delete
bool push(T *element)
uint16_t get_and_reset_dropped_count()
FreeRTOSQueue & operator=(const FreeRTOSQueue &)=delete
FreeRTOSQueue & operator=(FreeRTOSQueue &&)=delete
unsigned count
Definition gif.cpp:51