ESPHome 2026.8.0b4
Loading...
Searching...
No Matches
ln882h_ble.h
Go to the documentation of this file.
1#pragma once
2
4
5#ifdef USE_LN882H_BLE
6
11
12#include <atomic>
13#include <cstdint>
14
15namespace esphome::ln882h_ble {
16
17enum class BLEComponentState : uint8_t {
18 STATE_OFF = 0,
20 ACTIVE,
21};
22
26 uint8_t mac[MAC_ADDRESS_SIZE]; // as the controller delivers it (LSB-first)
27 int8_t rssi; // signed dBm (-127..+20)
28 uint8_t addr_type;
29 bool is_scan_response; // report is a scan response (active scan)
30 bool scannable; // advertisement may be followed by a scan response
31 uint8_t data_len; // bytes valid in data[] (<= 62)
32 // Each report carries ONE frame — a legacy advertisement (<=31 B) or a scan
33 // response (<=31 B) — delivered split, exactly as the SDK reports them. The
34 // TRACKER merges the pair into a single frame before any consumer sees it
35 // (Bluedroid semantics, HubCapabilities::merges_scan_response). 62 is twice
36 // the legacy maximum: defensive headroom for the data_len clamp, and the
37 // same width as the merged framing downstream.
38 uint8_t data[62];
39
40 // EventPool contract: nothing is heap-allocated inside a report.
41 void release() {}
42};
43
49 public:
50 virtual void on_scan_report(const BLEScanReport &report) = 0;
51
52 protected:
53 ~BLEScanListener() = default; // deletion via this interface is not part of the contract
54};
55
56// Maximum reports buffered between the rw task and loop(). Sized from the
57// measured worst case, not copied: WiFi/BLE coexistence delays rw-task report
58// delivery by up to ~136 ms on this device (see the tracker's pending-adv
59// timeout rationale), and a busy 2.4 GHz environment delivers ~200-400
60// reports/s — a stall plus one loop() interval buffers ~30-60 reports, so 63
61// usable slots absorb it with margin. ~4.7 KB at high water, reached only
62// during such stalls.
63static constexpr uint8_t MAX_SCAN_REPORT_QUEUE_SIZE = 64;
64
65// Rejected frames tolerated before the first delivered report without
66// declaring the scanner dead (boot-time stray extended frames are normal).
67static constexpr uint16_t REJECTED_DEAD_SCANNER_THRESHOLD = 16;
68
69class LN882HBLE final : public Component {
70 public:
71 void setup() override;
72 void loop() override;
73 void dump_config() override;
74 float get_setup_priority() const override;
75
80 void enable();
81 bool is_active() const { return this->state_ == BLEComponentState::ACTIVE; }
82
83 void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; }
84
89 void get_mac_lsb_first(uint8_t out[6]) const;
90
91#ifdef LN882H_BLE_SCAN_LISTENER_COUNT
95 void register_scan_listener(BLEScanListener *listener) { this->scan_listeners_.push_back(listener); }
96#endif
97
102 void scan_start(uint16_t interval, uint16_t window, bool active);
104 void scan_stop();
105
112 void push_scan_report(BLEScanReport *report);
116 void count_rejected_report() { this->rejected_reports_.fetch_add(1, std::memory_order_relaxed); }
117
118 protected:
119 void resolve_mac_();
120
121#ifdef LN882H_BLE_SCAN_LISTENER_COUNT
122 // Codegen-sized: no heap allocation, no std::vector template instantiation —
123 // the same StaticVector pattern as the tracker's ble_device_base listeners.
125#endif
126 // Report ring: the SDK event callback (rw task) allocates a report from the
127 // pool, fills it and pushes the pointer; loop() pops, dispatches and releases.
128 // Lock-free SPSC, zero allocation at steady state — the esp32_ble pattern.
129 // Overflow drops the NEWEST report (allocate fails, producer counts and
130 // returns) — under a coexistence stall the freshest advertisements are lost
131 // while queued ones drain. Deliberate: matches esp32_ble, and dropping from
132 // the head would need consumer-side locking this design exists to avoid.
134 // Pool sized to queue capacity (SIZE-1): the ring reserves one slot, so
135 // allocate() returns nullptr before push() can fail. This prevents leaking a
136 // pool slot on a failed push and keeps release() off the producer path.
137 esphome::EventPool<BLEScanReport, MAX_SCAN_REPORT_QUEUE_SIZE - 1> report_pool_;
138 // Reports rejected by the legacy-only filter (rw-task producer, main-task
139 // consumer via exchange in loop()).
140 std::atomic<uint16_t> rejected_reports_{0};
141 uint8_t ble_mac_[MAC_ADDRESS_SIZE]{0}; // controller (LSB-first) order, as ln_bd_addr_t stores it
143 bool enable_on_boot_{false};
144 bool scanning_{false}; // controller scan running (re-entry guard for scan_start)
145 // Dead-scanner diagnosis: done once a report is delivered or the one-shot
146 // warning has fired, whichever comes first.
148 uint32_t rejected_before_delivery_{0}; // drives the dead-scanner warning
149};
150
151} // namespace esphome::ln882h_ble
152
153#endif // USE_LN882H_BLE
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:227
Consumer interface for controller scan reports.
Definition ln882h_ble.h:48
virtual void on_scan_report(const BLEScanReport &report)=0
void count_rejected_report()
Internal, rw-task context: count a report rejected by the legacy-only filter, so a wrong assumption a...
Definition ln882h_ble.h:116
esphome::EventPool< BLEScanReport, MAX_SCAN_REPORT_QUEUE_SIZE - 1 > report_pool_
Definition ln882h_ble.h:137
float get_setup_priority() const override
void get_mac_lsb_first(uint8_t out[6]) const
Controller BLE address in the SDK's ln_bd_addr_t order: least-significant octet first (the BLE/HCI co...
esphome::LockFreeQueue< BLEScanReport, MAX_SCAN_REPORT_QUEUE_SIZE > report_queue_
Definition ln882h_ble.h:133
BLEScanReport * allocate_scan_report()
Internal, SDK rw-task event-callback context: allocate a pool slot for a scan report.
void set_enable_on_boot(bool enable_on_boot)
Definition ln882h_ble.h:83
void register_scan_listener(BLEScanListener *listener)
Register a consumer for scan reports (delivered on the main task via loop()).
Definition ln882h_ble.h:95
std::atomic< uint16_t > rejected_reports_
Definition ln882h_ble.h:140
StaticVector< BLEScanListener *, LN882H_BLE_SCAN_LISTENER_COUNT > scan_listeners_
Definition ln882h_ble.h:124
uint8_t ble_mac_[MAC_ADDRESS_SIZE]
Definition ln882h_ble.h:141
void push_scan_report(BLEScanReport *report)
Internal: hand a filled slot to the main-task queue (cannot fail — the pool is sized to the queue cap...
void enable()
Bring up the LN882H BLE stack (one-time; the SDK has no teardown path).
void scan_stop()
Stop the controller scan (no-op when not scanning).
uint32_t * scan_start
static void uint32_t
One scan report from the controller, decoded from the SDK's rw-task event (RSSI already sign-correcte...
Definition ln882h_ble.h:25
uint8_t mac[MAC_ADDRESS_SIZE]
Definition ln882h_ble.h:26