ESPHome 2026.8.0b4
Loading...
Searching...
No Matches
ln882h_ble.cpp
Go to the documentation of this file.
1// ln882h_ble.cpp
2//
3// BLE controller support for the LN882H (LibreTiny lightning-ln882h family) —
4// the platform analog of esp32_ble / rp2040_ble. Owns everything that talks to
5// the LN882H BLE SDK:
6// - one-time stack bring-up (rw_init + the ln_* app init sequence),
7// - the controller BLE address (persistent KV entry, WiFi-MAC-derived once),
8// - the raw controller scan primitives (ln_ble_scan_start/stop),
9// - the scan-report ring: the SDK's rw-task event callback decodes each
10// report (including the controller's RSSI sign quirk) into a fixed pool
11// and pushes it on a lock-free SPSC queue; loop() drains, dispatches on
12// the main task and returns reports to the pool — the same EventPool +
13// LockFreeQueue handoff esp32_ble uses, zero allocation at steady state.
14// Consumers contain no SDK calls of their own.
15//
16// BLE stack init and scan lifecycle mirror the SDK's ble_app usage. The BLE
17// stack itself is compiled and linked by the LibreTiny lightning-ln882h builder
18// (CFG_SUPPORT_BLE=1 via custom_options.proj_config#h; prebuilt
19// libln882h_ble_full_stack.a).
20
21#include "ln882h_ble.h" // pulls esphome/core/defines.h for USE_LN882H_BLE
22
23#ifdef USE_LN882H_BLE
24
25#include <algorithm>
26#include <cstddef>
27#include <cstring>
28
29#include "esphome/core/hal.h"
30#include "esphome/core/helpers.h" // get_mac_address_raw()
31#include "esphome/core/log.h"
32
33// ---------------------------------------------------------------------------
34// LN882H BLE SDK — forward declarations
35// ---------------------------------------------------------------------------
36extern "C" {
37
38struct ln_bd_addr_v_t { // NOLINT(readability-identifier-naming) - mirrors the SDK type name
39 uint8_t addr[6];
40}; // ABI-identical to ln_bd_addr_t
42struct ln_bd_addr_v_t *ln_kv_ble_pub_addr_get(void);
43int ln_kv_ble_addr_store(struct ln_bd_addr_v_t addr);
45
46void rw_init(uint8_t mac[6]);
47void ln_gap_app_init(void);
51void ln_ble_smp_init(void);
54void ln_gap_reset(void);
55
57void ln_ble_scan_start(void *scan_param);
59
60using ble_evt_cb_t = void (*)(void *param);
62
63} // extern "C"
64
65// ln_bd_addr_v_t mirrors the SDK's ln_bd_addr_t (ln_ble_app_defines.h) and is
66// passed to ln_kv_ble_addr_store() by value, so its size and alignment are part
67// of the calling convention.
68static_assert(sizeof(struct ln_bd_addr_v_t) == 6, "ln_bd_addr_v_t must match the SDK's ln_bd_addr_t layout");
69static_assert(alignof(struct ln_bd_addr_v_t) == 1, "ln_bd_addr_v_t must stay byte-aligned like the SDK type");
70
71// ---------------------------------------------------------------------------
72// LN882H SDK constants
73// CLK_G_BLE — hal/hal_clock.h clock gate bit for the BLE block
74// BLE_EVT_ID_SCAN_REPORT — ble/ble_evt.h event id for scan reports
75// GAPM_* — ble/mac/ble/hl/api/gapm_task.h, enums gapm_scan_type /
76// gapm_dup_filter_pol / gapm_scan_prop / gapm_adv_report_info
77// ---------------------------------------------------------------------------
78static constexpr uint32_t CLK_G_BLE = 1u << 0;
79static constexpr int BLE_EVT_ID_SCAN_REPORT = 3;
80
81// WiFi/BLE packet-traffic-indication (PTI) arbitration register. The LN882H SDK
82// exposes no symbolic name for this register; the address and value replicate
83// the SDK reference bring-up. 0x003F sets all six PTI priority bits so the
84// arbiter can pre-empt WiFi for BLE traffic.
85static constexpr uint32_t BLE_COEX_PTI_REG_ADDR = 0x400121F8;
86static constexpr uint32_t BLE_COEX_PTI_ENABLE_ALL = 0x003F;
87
88// ble_app_default_cfg.h BLE_DEFAULT_PUBLIC_ADDR, in the SDK's ln_bd_addr_t
89// array order — least-significant octet first, the BLE/HCI convention (the
90// SDK's own AT commands print addr[5]..addr[0]). Printable form:
91// 00:FF:03:12:34:56.
92static constexpr uint8_t BLE_DEFAULT_ADDR[6] = {0x56, 0x34, 0x12, 0x03, 0xFF, 0x00};
93
94// The SDK's KV loader treats an all-zero address as unset and substitutes the
95// default; resolve_mac_() applies the same rule to a stored entry.
96static bool is_unset_addr(const uint8_t (&addr)[6]) {
97 return std::all_of(std::begin(addr), std::end(addr), [](uint8_t b) { return b == 0; });
98}
99
100// gapm_scan_type: GEN_DISC = 0, LIM_DISC = 1, OBSERVER = 2. Observer reports every
101// advertisement without filtering — what a tracker wants.
102static constexpr uint8_t GAPM_SCAN_TYPE_OBSERVER = 2;
103static constexpr uint8_t GAPM_DUP_FILT_DIS = 0;
104// gapm_scan_prop bits: PHY_1M = 1<<0, PHY_CODED = 1<<1, ACTIVE_1M = 1<<2, ACTIVE_CODED = 1<<3.
105static constexpr uint8_t GAPM_SCAN_PROP_PHY_1M_BIT = 1 << 0;
106static constexpr uint8_t GAPM_SCAN_PROP_ACTIVE_1M_BIT = 1 << 2;
107
108// GAPM extended-advertising report types (bits 2:0 of ble_scan_report_t::info).
109// 0 = ADV_EXT (extended advertisement), 1 = ADV_LEG (legacy advertisement),
110// 2 = SCAN_RSP_EXT (scan response to extended adv), 3 = SCAN_RSP_LEG (scan response to legacy adv).
111static constexpr uint8_t GAPM_REPORT_TYPE_ADV_LEG = 1;
112static constexpr uint8_t GAPM_REPORT_TYPE_SCAN_RSP_LEG = 3;
113// Bit 5 of ble_scan_report_t::info: the advertisement is scannable, i.e. a scan
114// response may follow (enum gapm_adv_report_info, GAPM_REPORT_INFO_SCAN_ADV_BIT).
115static constexpr uint8_t GAPM_REPORT_INFO_SCAN_ADV_BIT = 1u << 5;
116
117// ---------------------------------------------------------------------------
118// SDK struct layouts
119// ---------------------------------------------------------------------------
120
121// Scan parameter block passed to ln_ble_scan_start(); mirrors the SDK layout,
122// with the pad byte explicit so the whole block zero-initialises.
123struct le_scan_parameters_t { // NOLINT(readability-identifier-naming) - mirrors the SDK type name
124 uint8_t type;
125 uint8_t prop;
126 uint8_t dup_filt_pol;
127 uint8_t pad;
128 uint16_t scan_intv;
129 uint16_t scan_wd;
130};
131// Pin the compiler's layout decisions for the hand-mirrored SDK struct: it is
132// passed to ln_ble_scan_start() as void*, so a padding drift would silently
133// feed garbage scan parameters to the controller.
134static_assert(sizeof(le_scan_parameters_t) == 8, "le_scan_parameters_t must match the SDK layout");
135static_assert(offsetof(le_scan_parameters_t, scan_intv) == 4, "unexpected padding in le_scan_parameters_t");
136
137// Scan report delivered by the BLE_EVT_ID_SCAN_REPORT event. Layout verified on
138// hardware against the prebuilt BLE stack LibreTiny links: its report carries no
139// PHY fields and stores the advertisement data inline (flexible array), unlike
140// the newer upstream SDK header (which adds phy_prim/phy_second and a data pointer).
141struct ble_scan_report_t { // NOLINT(readability-identifier-naming) - mirrors the SDK type name
142 uint8_t actv_idx;
143 uint8_t info;
144 uint8_t trans_addr_type;
145 uint8_t trans_addr[6];
146 uint8_t target_addr_type;
147 uint8_t target_addr[6];
148 int8_t tx_pwr;
149 int8_t rssi; // signed dBm, range -127..+20 (ble_evt_scan_report_t from ln_ble_event_manager.h)
150 uint16_t length;
151 uint8_t data[0];
152};
153// Pin the layout of the hand-mirrored report struct too: the comment above
154// notes a newer SDK header uses a different layout (PHY fields + data pointer),
155// so silent drift here would corrupt every decoded advertisement.
156static_assert(sizeof(ble_scan_report_t) == 20, "ble_scan_report_t must match the linked BLE stack's layout");
157static_assert(offsetof(ble_scan_report_t, length) == 18, "unexpected padding in ble_scan_report_t");
158static_assert(offsetof(ble_scan_report_t, data) == 20, "advertisement data must follow the header inline");
159
160// ---------------------------------------------------------------------------
161// __sprintf weak stub
162//
163// The LN882H BLE SDK objects reference __sprintf (a Beken/LN libc alias) that
164// LibreTiny's newlib does not provide. Supply a weak fallback so linking
165// succeeds; a real definition, if one is ever provided, takes precedence.
166// ---------------------------------------------------------------------------
167#include <cstdarg>
168#include <cstdio>
169extern "C" __attribute__((weak)) int
170__sprintf( // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp,readability-identifier-naming)
171 char *str, const char *format, ...) {
172 va_list args;
174 int ret = vsprintf(str, format, args); // NOLINT
175 va_end(args);
176 return ret;
177}
178
180
181static const char *const TAG = "ln882h_ble";
182
183// The SDK event callback is a plain C function pointer with no user argument,
184// so it reaches the (single) component instance through a file-static pointer.
185static LN882HBLE *s_ble = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
186
187// Scan parameter blocks handed to ln_ble_scan_start(void *). static storage:
188// the SDK may retain the pointer past the call (the block travels into a GAPM
189// message consumed later by the rw task), so a stack-local would leave the
190// controller reading a dead frame. Double-buffered: consecutive starts (the
191// enable() probe followed by the first real scan, or a parameter restart)
192// alternate blocks, so a rewrite can never race a previous block that is still
193// in flight — correct under either reading of SDK retention. All writers run
194// on the main task.
195static le_scan_parameters_t s_scan_params[2]{}; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
196static uint8_t s_scan_params_idx = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
197
198static le_scan_parameters_t *next_scan_params() {
199 s_scan_params_idx ^= 1;
200 return &s_scan_params[s_scan_params_idx];
201}
202
203// ---------------------------------------------------------------------------
204// Scan-report event callback — runs in the SDK's rw task context.
205// Decode the report (hardware-verified struct layout + the RSSI sign fix),
206// copy it into the queue and return; all dispatch happens in loop() on the
207// main task.
208// ---------------------------------------------------------------------------
209static void ble_scan_callback(void *param) {
210 if (s_ble == nullptr || param == nullptr)
211 return;
212 const auto *info = reinterpret_cast<const ble_scan_report_t *>(param);
213
214 // Only legacy framing is supported (see scan_start(): legacy 1M PHY only):
215 // an extended report does not fit BLEScanReport::data and would reach
216 // consumers as a truncated legacy frame. Reject before allocating so these
217 // do not burn pool slots either.
218 const uint8_t report_type = info->info & 0x07;
219 if (report_type != GAPM_REPORT_TYPE_ADV_LEG && report_type != GAPM_REPORT_TYPE_SCAN_RSP_LEG) {
220 s_ble->count_rejected_report();
221 return;
222 }
223
224 // Fill the pool slot in place (the bk72xx_ble shape): no report on the rw
225 // task's stack — its size is fixed by the prebuilt stack — one copy of the
226 // payload instead of two, and only data_len bytes ever leave this frame.
227 BLEScanReport *slot = s_ble->allocate_scan_report();
228 if (slot == nullptr)
229 return; // no slot — counted as dropped in allocate_scan_report()
230
231 // BLE RSSI sign fix. The LN882H controller intermittently reports the RSSI with
232 // a flipped sign: a real -58 dBm arrives as +58, above the SDK's documented
233 // -127..+20 dBm maximum. Recover it by negating any value above +20 (verified
234 // on-device: the out-of-range positives cluster at the magnitude of each
235 // device's real readings). This is the ONLY LN882H-specific RSSI handling —
236 // downstream the value is used exactly like on ESP32.
237 const int8_t raw = info->rssi;
238
239 memcpy(slot->mac, info->trans_addr, MAC_ADDRESS_SIZE);
240 slot->rssi = (raw > 20) ? static_cast<int8_t>(-raw) : raw;
241 slot->addr_type = info->trans_addr_type;
242 slot->is_scan_response = report_type == GAPM_REPORT_TYPE_SCAN_RSP_LEG;
243 slot->scannable = (info->info & GAPM_REPORT_INFO_SCAN_ADV_BIT) != 0;
244 slot->data_len = (info->length <= sizeof(slot->data)) ? static_cast<uint8_t>(info->length)
245 : static_cast<uint8_t>(sizeof(slot->data));
246 memcpy(slot->data, info->data, slot->data_len);
247
248 s_ble->push_scan_report(slot);
249}
250
252 BLEScanReport *slot = this->report_pool_.allocate();
253 if (slot == nullptr) {
254 // No slot: pool exhausted (queue full) or the pool's on-demand RAM
255 // allocation failed; count and drop either way.
256 this->report_queue_.increment_dropped_count();
257 }
258 return slot;
259}
260
262 // Cannot fail: the pool is sized to the queue capacity.
263 this->report_queue_.push(report);
264}
265
266// ---------------------------------------------------------------------------
267// Component lifecycle
268// ---------------------------------------------------------------------------
269
271 s_ble = this;
272 // Resolve the MAC early so get_mac_lsb_first() is valid for consumers before
273 // the stack is up. The KV load also happens here (no stack dependency).
274 this->resolve_mac_();
275 if (this->enable_on_boot_) {
276 this->enable();
277 }
278}
279
280// AFTER_WIFI, not BLUETOOTH: replicates the proven pre-split timing — the LN
281// SDK's KV subsystem is first touched only once WiFi is up; earlier access
282// destabilized the device in hardware testing.
284
287 return;
289
290 *reinterpret_cast<volatile uint32_t *>(BLE_COEX_PTI_REG_ADDR) = BLE_COEX_PTI_ENABLE_ALL;
292
293 rw_init(this->ble_mac_);
301 ln_gap_reset();
302
303 delay(100); // NOLINT — one-time BLE stack init; SDK requires this settle time
304
306 delay(10);
307
308 // Prime the scan activity with a short probe start/stop — the SDK's scan
309 // manager completes activity creation on the first start. Uses the shared
310 // static parameter block (see s_scan_params for the lifetime rationale).
311 le_scan_parameters_t *probe = next_scan_params();
312 probe->type = GAPM_SCAN_TYPE_OBSERVER;
313 probe->prop = GAPM_SCAN_PROP_PHY_1M_BIT;
314 probe->dup_filt_pol = GAPM_DUP_FILT_DIS;
315 probe->scan_intv = 160;
316 probe->scan_wd = 16;
317 ln_ble_scan_start(probe);
318 delay(10);
320
321 // Register the scan-report event exactly once, after the event manager is up.
322 // Repeated registration corrupts the SDK's event registry (verified on
323 // hardware), which is why this lives here and not in scan_start().
324 ln_ble_evt_mgr_reg_evt(BLE_EVT_ID_SCAN_REPORT, ble_scan_callback);
325
327 ESP_LOGD(TAG, "BLE stack initialised");
328}
329
331 // Log dropped reports before the empty-queue return: a drop can also mean
332 // EventPool::allocate() failed on heap exhaustion, and that can happen with
333 // the queue empty — from the very first report on. Checking here keeps that
334 // failure visible instead of producing a scanner that is silently dead.
335 uint16_t dropped = this->report_queue_.get_and_reset_dropped_count();
336 if (dropped > 0)
337 ESP_LOGW(TAG, "Dropped %u scan reports (queue full or out of memory for a report slot)", dropped);
338 // Drain the lock-free ring filled by the rw task; all per-report work runs
339 // here on the main task, then the report returns to the pool.
340 BLEScanReport *report = this->report_queue_.pop();
341 if (report != nullptr) {
342 this->reject_diagnosis_done_ = true;
343 do {
344#ifdef LN882H_BLE_SCAN_LISTENER_COUNT
345 for (auto *listener : this->scan_listeners_)
346 listener->on_scan_report(*report);
347#endif
348 this->report_pool_.release(report);
349 } while ((report = this->report_queue_.pop()) != nullptr);
350 }
351
352 // Rejected-report accounting AFTER the drain: a stray non-legacy frame
353 // arriving ahead of the first good one must not latch the dead-scanner
354 // warning; the threshold keeps one-off boot noise below it while a truly
355 // dead scanner (~200 reports/s all rejected) crosses it within a second.
356 // Avoid the sub-word CAS in the common case (LockFreeQueue's dropped-count
357 // pattern): rejects are rare, the load is cheap.
358 uint16_t rejected = this->rejected_reports_.load(std::memory_order_relaxed);
359 if (rejected > 0) {
360 rejected = this->rejected_reports_.exchange(0, std::memory_order_relaxed);
361 if (!this->reject_diagnosis_done_) {
362 this->rejected_before_delivery_ += rejected;
363 if (this->rejected_before_delivery_ >= REJECTED_DEAD_SCANNER_THRESHOLD) {
364 this->reject_diagnosis_done_ = true;
365 ESP_LOGW(TAG, "Rejected %u scan reports before any was delivered - unexpected report encoding?",
366 static_cast<unsigned>(this->rejected_before_delivery_));
367 }
368 }
369 ESP_LOGV(TAG, "Rejected %u non-legacy scan reports", rejected);
370 }
371}
372
373void LN882HBLE::get_mac_lsb_first(uint8_t out[6]) const { memcpy(out, this->ble_mac_, sizeof(this->ble_mac_)); }
374
376 ESP_LOGCONFIG(TAG,
377 "LN882H BLE:\n"
378 " MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n"
379 " Active: %s",
380 this->ble_mac_[5], this->ble_mac_[4], this->ble_mac_[3], this->ble_mac_[2], this->ble_mac_[1],
381 this->ble_mac_[0], YESNO(this->is_active()));
382}
383
384// ---------------------------------------------------------------------------
385// MAC resolution
386// ---------------------------------------------------------------------------
387
390
391 // ln_kv_ble_app_init() loads the persistent address from the "2_ble_addr" KV
392 // entry, falling back to BLE_DEFAULT_ADDR when nothing is stored. A stored
393 // address is preferred so boot does not write flash. Order is LSB-first
394 // throughout (BLE/HCI convention); consumers reverse for printable form.
395 ln_bd_addr_v_t bt_addr{};
396 bool have_unique_addr = false;
397 if (const ln_bd_addr_v_t *stored = ln_kv_ble_pub_addr_get(); stored != nullptr) {
398 bt_addr = *stored;
399 // All-zero is "unset", not a unique address: ln_kv_ble_addr_load() itself
400 // substitutes the default for it, so programming it verbatim would give the
401 // controller a null address. Treat it like the default and derive instead.
402 have_unique_addr =
403 memcmp(bt_addr.addr, BLE_DEFAULT_ADDR, sizeof(bt_addr.addr)) != 0 && !is_unset_addr(bt_addr.addr);
404 } else {
405 // KV subsystem down (wrong partition layout, corrupted region): derive
406 // below instead of dereferencing null and boot-looping.
407 ESP_LOGW(TAG, "BLE address KV unavailable; deriving address from WiFi MAC");
408 }
409 if (!have_unique_addr) {
410 uint8_t wifi_mac[MAC_ADDRESS_SIZE] = {0};
411 get_mac_address_raw(wifi_mac); // MSB-first
412 // Reverse into controller (LSB-first) order, then BLE = WiFi + 1: increment
413 // the NIC low byte (addr[0] once reversed), no carry, OUI unchanged — the
414 // Beken/Tuya factory pairing the bk72xx sibling also uses.
415 for (int i = 0; i < 6; i++)
416 bt_addr.addr[i] = wifi_mac[5 - i];
417 bt_addr.addr[0] = static_cast<uint8_t>(bt_addr.addr[0] + 1);
418 if (int err = ln_kv_ble_addr_store(bt_addr); err != 0) {
419 ESP_LOGW(TAG, "Failed to persist derived BLE address (err %d); will re-derive next boot", err);
420 } else {
421 ESP_LOGD(TAG, "MAC derived (WiFi+1) and stored");
422 }
423 }
424 memcpy(this->ble_mac_, bt_addr.addr, MAC_ADDRESS_SIZE);
425}
426
427// ---------------------------------------------------------------------------
428// Controller scan primitives
429// ---------------------------------------------------------------------------
430
431void LN882HBLE::scan_start(uint16_t interval, uint16_t window, bool active) {
432 if (!this->is_active())
433 this->enable();
434
435 if (this->scanning_) {
436 // Already scanning - stop first so this call cleanly restarts with the new
437 // parameters (re-entry guard). Give the GAPM stop the same settle time
438 // enable() grants between consecutive GAPM operations before restarting.
439 this->scan_stop();
440 delay(10); // NOLINT — restart-only, mirrors enable()'s inter-operation settle
441 }
442
443 // Double-buffered static block — see s_scan_params for the lifetime rationale.
444 le_scan_parameters_t *p = next_scan_params();
445 p->dup_filt_pol = GAPM_DUP_FILT_DIS;
446 p->type = GAPM_SCAN_TYPE_OBSERVER;
447 p->scan_intv = interval;
448 p->scan_wd = window;
449 // Legacy 1M PHY only: consumers size their buffers for legacy advertisements
450 // (62 B); coded/extended PHY (up to 255 B) would be silently truncated.
451 p->prop = GAPM_SCAN_PROP_PHY_1M_BIT;
452 if (active)
453 p->prop |= GAPM_SCAN_PROP_ACTIVE_1M_BIT;
454
456 // ln_ble_scan_start() returns void, so this tracks the requested state, not a
457 // confirmed one — a controller-side failure surfaces as an idle scanner (no
458 // reports), which the consumer's start retry/backoff owns.
459 this->scanning_ = true;
460}
461
463 // No-op when idle, as documented: the guard keeps a redundant SDK stop off
464 // the GAPM path (scan_start()'s re-entry guard calls this while scanning).
465 if (!this->scanning_)
466 return;
468 this->scanning_ = false;
469}
470
471} // namespace esphome::ln882h_ble
472
473#endif // USE_LN882H_BLE
uint8_t raw[35]
Definition bl0939.h: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.
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 scan_start(uint16_t interval, uint16_t window, bool active)
Start the controller scan.
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).
struct @66::@67 __attribute__
Wake the main loop task from an ISR. ISR-safe.
Definition main_task.h:32
void ln_rw_app_task_init(void)
void ln_ble_smp_init(void)
int ln_kv_ble_addr_store(struct ln_bd_addr_v_t addr)
void ln_ble_scan_mgr_init(void)
void ln_ble_scan_start(void *scan_param)
int ret
va_end(args)
void ln_ble_conn_mgr_init(void)
void ln_kv_ble_app_init(void)
void ln_ble_evt_mgr_init(void)
const char * format
void ln_ble_evt_mgr_reg_evt(int evt_id, ble_evt_cb_t cb)
void ln_gap_reset(void)
void ln_ble_scan_stop(void)
void soc_module_clk_gate_enable(uint32_t clk)
const char va_start(args, format)
void rw_init(uint8_t mac[6])
void ln_ble_scan_actv_creat(void)
void(*)(void *param) ble_evt_cb_t
void ln_gap_app_init(void)
struct ln_bd_addr_v_t * ln_kv_ble_pub_addr_get(void)
void ln_gatt_app_init(void)
static float float b
constexpr float AFTER_WIFI
For components that should be initialized after WiFi is connected.
Definition component.h:55
void get_mac_address_raw(uint8_t *mac)
Get the device MAC address as raw bytes, written into the provided byte array (6 bytes).
Definition helpers.cpp:74
void HOT delay(uint32_t ms)
Definition hal.cpp:85
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
uint16_t length
Definition tt21100.cpp:0