ESPHome 2026.8.0b4
Loading...
Searching...
No Matches
bk72xx_ble.cpp
Go to the documentation of this file.
1// bk72xx_ble.cpp
2//
3// BLE controller support for the BK72xx BLE-5.x chips (LibreTiny beken-72xx
4// family) — the platform analog of esp32_ble / rp2040_ble. Owns everything that
5// talks to the Beken BDK BLE stack:
6// - one-time stack bring-up (ble_set_notice_cb() + ble_entry()),
7// - the controller BLE address,
8// - the scan reconciler (request, pacing, bring-up budget) over the
9// bdk_scan surface,
10// - the scan-report ring: the BDK notice callback (BLE task) takes a report
11// from a fixed pool and pushes it on a lock-free SPSC queue; loop() drains,
12// dispatches on the main task and returns reports to the pool — the same
13// EventPool + LockFreeQueue handoff esp32_ble uses, zero allocation at
14// steady state.
15// Consumers contain no SDK calls of their own.
16//
17// NOTE: the Beken BDK BLE 5.x stack is compiled and linked by the LibreTiny
18// beken-72xx builder itself (prebuilt libble_<chip>.a + ble_5_x sources, gated
19// on CFG_SUPPORT_BLE / CFG_BLE_VERSION in sys_config.h). This component only
20// calls into it via the public ble_api.h — no framework patch is required.
21
22#include "bk72xx_ble.h" // pulls esphome/core/defines.h for USE_BK72XX_BLE
23
24#include "bdk_scan.h" // the raw BDK scan surface (state reads, starts, release)
25
26#ifdef USE_BK72XX_BLE
27
28#include <cstring>
29
31#include "esphome/core/hal.h"
32#include "esphome/core/helpers.h" // get_mac_address_raw()
33#include "esphome/core/log.h"
34
35// ---------------------------------------------------------------------------
36// SDK-capability gate (not a chip allowlist).
37// This component drives the Beken BLE *5.x* controller. `ble_api.h` cannot be
38// the probe: it ships for every SoC (driver/include) and merely switches on
39// CFG_BLE_VERSION internally. `app_ble.h` is on the include path only when the
40// LibreTiny beken-72xx builder selects a 5.x stack, so gating on it supports
41// any BLE-5.x chip — present or future — without a hard-coded list, and a
42// non-5.x build fails here with a clear message instead of a cryptic
43// "app_ble.h: No such file or directory".
44// ---------------------------------------------------------------------------
45#if defined(CLANG_TIDY)
46// The clang-tidy environment does not carry the full Beken BDK BLE 5.x API
47// (its ble_api.h variant lacks parts of the 5.x surface), so there is nothing
48// accurate to analyze the SDK calls against — skip the file under analysis.
49#define BK72XX_BLE_NO_SDK
50#elif !__has_include("ble_api.h") || !__has_include("app_ble.h")
51// Also skip the SDK body: #error does not stop the preprocessor, and on a 4.2
52// SoC ble_api.h exists, so without the guard the 5.x symbols would fail one by
53// one and bury this message.
54#define BK72XX_BLE_NO_SDK
55#error \
56 "bk72xx_ble requires a BLE 5.x Beken SDK (app_ble.h). Supported SoCs: BK7231N/BK7236 (BLE 5.1) and BK7238/BK7252N/BK7253 (BLE 5.2). BK7231T/BK7251/BK7271 (BLE 4.2) and BK7231Q (no BLE) are not supported."
57#endif
58
59#ifndef BK72XX_BLE_NO_SDK
60
61// ---------------------------------------------------------------------------
62// Beken BDK BLE 5.x SDK — public API.
63// Exposed on the include path by the LibreTiny beken-72xx builder
64// (cores/.../ble_5_x_rw + driver/include). Wrapped in extern "C" because these
65// are C headers consumed from C++ (a standard C-header-from-C++ pattern).
66// ---------------------------------------------------------------------------
67extern "C" {
68#include "ble_api.h" // ble_set_notice_cb, recv_adv_t, ble_notice_t,
69 // BLE_5_REPORT_ADV (scan primitives live in bdk_scan.cpp)
70#ifdef BK72XX_BLE_HAS_COMMON_BDADDR
71#include "common_bt_defines.h" // struct bd_addr
72// The controller's public BLE address, populated by the BDK during ble_entry().
73// Present on BK7231N; the other BLE-5.x chips' stacks have no such symbol — there the
74// address is derived from the WiFi MAC instead (matching the BDK's own fallback).
75extern struct bd_addr common_default_bdaddr;
76#endif
77// ble_entry() brings up the BDK BLE stack; it is not declared in ble_api.h, so
78// declare it here.
79void ble_entry(void);
80}
81
82namespace esphome::bk72xx_ble {
83
84static const char *const TAG = "bk72xx_ble";
85
86static constexpr uint32_t RECONCILE_RETRY_MS = 10; // pump floor for fast loops
87static constexpr uint32_t RECONCILE_REJECTED_RETRY_MS = 500; // retry gate after a rejected release
88static constexpr uint32_t RECONCILE_PENDING_TIMEOUT_MS = 2000; // bring-up budget before FAILED
89static constexpr uint32_t SCAN_LIVENESS_CHECK_MS = 1000; // settled-scan re-check cadence
90static constexpr uint32_t TEARDOWN_STUCK_ERROR_MS = 30000; // stuck-teardown ERROR (stop also goes FAILED)
91
92// The BDK notice callback is a plain C function pointer with no user argument,
93// so it reaches the (single) component instance through a file-static pointer.
94static BK72xxBLE *s_ble = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
95
96// ---------------------------------------------------------------------------
97// BLE notice callback — runs in the BDK BLE task context.
98// The BK controller reports every advertisement as a BLE_5_REPORT_ADV notice
99// carrying a recv_adv_t. Copy it into the queue and return; all dispatch
100// happens in loop() on the main task.
101// ---------------------------------------------------------------------------
102static void ble_notice_callback(ble_notice_t notice, void *param) {
103 if (s_ble == nullptr || param == nullptr)
104 return;
105 if (notice != BLE_5_REPORT_ADV)
106 return;
107
108 const recv_adv_t *info = reinterpret_cast<const recv_adv_t *>(param);
109 // rssi is a signed dBm carried in a uint8_t; cast through int8_t (standard for
110 // a signed dBm value packed in a uint8_t).
111 s_ble->enqueue_scan_report(info->adv_addr, static_cast<int8_t>(info->rssi), info->adv_addr_type,
112 static_cast<uint8_t>(info->evt_type), info->data, info->data_len);
113}
114
115void BK72xxBLE::enqueue_scan_report(const uint8_t *mac, int8_t rssi, uint8_t addr_type, uint8_t evt_type,
116 const uint8_t *data, uint16_t data_len) {
117 BLEScanReport *report = this->report_pool_.allocate();
118 if (report == nullptr) {
119 // Pool exhausted — the queue is full; count and drop.
120 this->report_queue_.increment_dropped_count();
121 return;
122 }
123 memcpy(report->mac, mac, MAC_ADDRESS_SIZE);
124 report->rssi = rssi;
125 report->addr_type = addr_type;
126 report->evt_type = evt_type;
127 report->data_len =
128 (data_len <= sizeof(report->data)) ? static_cast<uint8_t>(data_len) : static_cast<uint8_t>(sizeof(report->data));
129 memcpy(report->data, data, report->data_len);
130 // Cannot fail: the pool is sized to the queue capacity.
131 this->report_queue_.push(report);
132}
133
134// ---------------------------------------------------------------------------
135// Component lifecycle
136// ---------------------------------------------------------------------------
137
139 s_ble = this;
140 // The report pool grows lazily on purpose: the BDK notice callback runs in
141 // task context (malloc-safe, unlike rp2040's IRQ path), and typical traffic
142 // stays far below the pool cap, so not warming contains RAM.
143 // Resolve the MAC early so get_mac_lsb_first() is valid for consumers before
144 // the stack is up (it is re-read once ble_entry() has run).
145 this->resolve_mac_();
146 if (this->enable_on_boot_) {
147 this->enable();
148 }
149}
150
151// AFTER_WIFI, not BLUETOOTH: replicates the proven pre-split timing — the BDK
152// is first touched only once WiFi is up (single-core WiFi/BLE bring-up order).
154
157 return;
159
160 // One-time BLE stack init: register the notice callback, then bring up the
161 // BDK BLE stack. The BDK has no teardown path — init happens at most once.
162 ble_set_notice_cb(ble_notice_callback);
163 ble_entry();
164
165 delay(100); // NOLINT — one-time BLE stack init; the SDK needs this settle time
166
167 // Re-read the BLE MAC now that the controller is up (common_default_bdaddr is
168 // populated by ble_entry()); resolve_mac_() may have fallen back earlier.
169 this->resolve_mac_();
170
171#ifdef BK72XX_BLE_HAS_COMMON_BDADDR
172 // Liveness heuristic (BK7231N): a healthy ble_entry() populates
173 // common_default_bdaddr during init, so all-zero after the settle delay
174 // suggests the stack did not come up. The BDK entry point returns void — no
175 // return code exists — so warn rather than fail: scan starts against a dead
176 // stack already fail cleanly downstream (no idle activity handle).
177 bool bdaddr_live = false;
178 for (uint8_t b : common_default_bdaddr.addr) {
179 if (b != 0) {
180 bdaddr_live = true;
181 break;
182 }
183 }
184 if (!bdaddr_live)
185 ESP_LOGW(TAG, "Controller address still unset after init; BLE stack may not have started");
186#endif
187
189 ESP_LOGD(TAG, "BLE stack initialised");
190}
191
193 // Keep reconciling toward the requested scan state (e.g. complete a stop
194 // that arrived while a controller operation was in flight), and re-check a
195 // settled scan at low frequency: a controller-side drop re-enters the
196 // bring-up, and the budget's FAILED feeds the tracker's recovery.
197 // Keep driving until settled: any PENDING, plus a terminal stop whose slot
198 // must still be freed. A FAILED scan request is the one combination not
199 // re-driven here — that belongs to the tracker's backoff.
200 const uint32_t pump_now = App.get_loop_component_start_time();
201 if (this->last_result_ == ScanOpResult::PENDING ||
202 (!this->scan_wanted_ && this->last_result_ == ScanOpResult::FAILED)) {
203 const uint32_t gate = (this->release_warned_ || this->last_result_ == ScanOpResult::FAILED)
204 ? RECONCILE_REJECTED_RETRY_MS
205 : RECONCILE_RETRY_MS;
206 if (pump_now - this->last_advance_ms_ >= gate)
207 this->advance_();
208 } else if (this->scan_wanted_ && this->last_result_ == ScanOpResult::SETTLED &&
209 pump_now - this->last_advance_ms_ >= SCAN_LIVENESS_CHECK_MS) {
210 // Re-check a settled scan; scan_start() refills the bring-up budget.
211 // WARN: the only report of a drop that recovers inside its budget.
212 if (this->scan_start(this->requested_.interval, this->requested_.window, this->requested_.active) !=
214 ESP_LOGW(TAG, "Controller dropped the scan; restarting");
215 }
216
217 // Drain the lock-free ring filled by the BLE task; all per-report work runs
218 // here on the main task, then the report returns to the pool.
219 BLEScanReport *report = this->report_queue_.pop();
220 if (report == nullptr)
221 return;
222 do {
223#ifdef BK72XX_BLE_SCAN_LISTENER_COUNT
224 for (auto *listener : this->scan_listeners_)
225 listener->on_scan_report(*report);
226#endif
227 this->report_pool_.release(report);
228 } while ((report = this->report_queue_.pop()) != nullptr);
229
230 // Log dropped reports — only reachable when reports were processed; drops can
231 // only occur while the queue is full, and only this loop drains it.
232 uint16_t dropped = this->report_queue_.get_and_reset_dropped_count();
233 if (dropped > 0)
234 ESP_LOGW(TAG, "Dropped %u scan reports due to queue overflow", dropped);
235}
236
237void BK72xxBLE::get_mac_lsb_first(uint8_t out[MAC_ADDRESS_SIZE]) const {
238 for (int i = 0; i < 6; i++)
239 out[i] = this->ble_mac_[i];
240}
241
243 // ble_mac_ is stored LSB-first (BLE convention); print [5..0] for the
244 // MSB-first order Home Assistant shows.
245 ESP_LOGCONFIG(TAG,
246 "BK72xx BLE:\n"
247 " MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n"
248 " Active: %s",
249 this->ble_mac_[5], this->ble_mac_[4], this->ble_mac_[3], this->ble_mac_[2], this->ble_mac_[1],
250 this->ble_mac_[0], YESNO(this->is_active()));
251}
252
253// ---------------------------------------------------------------------------
254// MAC resolution
255// ---------------------------------------------------------------------------
256
258#ifdef BK72XX_BLE_HAS_COMMON_BDADDR
259 // BK7231N: the BDK populates common_default_bdaddr (LSB-first, BLE convention)
260 // during ble_entry(). It may still be zero before the stack is up; if so, fall
261 // through to the WiFi-derived MAC below.
262 bool nonzero = false;
263 for (uint8_t b : common_default_bdaddr.addr) {
264 if (b != 0) {
265 nonzero = true;
266 break;
267 }
268 }
269 if (nonzero) {
270 memcpy(this->ble_mac_, common_default_bdaddr.addr, MAC_ADDRESS_SIZE);
271 return;
272 }
273#endif
274 // Chips whose BLE stack does not export common_default_bdaddr (BK7238 and the other
275 // BLE-5.x SoCs), or BK7231N before the stack is up: derive the BLE MAC exactly as the
276 // Beken BDK does in bdaddr_env_init() — the WiFi STA MAC with only its last byte
277 // incremented (sta_mac[5] += 1, a plain byte increment with no carry into the next
278 // byte), OUI unchanged. This reproduces the address the controller advertises with
279 // (verified against the BK7231N BLE-5.1 and BK7252N/BK7238 BLE-5.2 SDK sources), so it
280 // matches on every device, including the last-byte == 0xFF edge that a 24-bit increment
281 // would carry differently.
282 uint8_t wifi_mac[MAC_ADDRESS_SIZE];
283 get_mac_address_raw(wifi_mac); // MSB-first
284 const uint8_t ble[MAC_ADDRESS_SIZE] = {wifi_mac[0], wifi_mac[1], wifi_mac[2],
285 wifi_mac[3], wifi_mac[4], static_cast<uint8_t>(wifi_mac[5] + 1)};
286 // Store LSB-first to match recv_adv_t adv_addr ordering.
287 for (int i = 0; i < 6; i++)
288 this->ble_mac_[i] = ble[5 - i];
289}
290
291// ---------------------------------------------------------------------------
292// Scan reconciler
293// ---------------------------------------------------------------------------
294
295// Episode boundary: fresh teardown deadline and error bookkeeping.
297 this->teardown_since_ms_ = 0;
298 this->restarting_ = false;
299 this->last_release_err_ = 0;
300}
301
302ScanOpResult BK72xxBLE::scan_start(uint16_t interval, uint16_t window, bool active) {
303 if (!this->is_active())
304 this->enable();
305
306 const ScanParams params{active, interval, window};
307 // A new episode refills the budget and gets a fresh teardown deadline; a
308 // re-call observing an in-flight bring-up (last result PENDING) must not.
309 if (this->last_result_ != ScanOpResult::PENDING || !this->scan_wanted_ || params != this->requested_) {
312 }
313 this->scan_wanted_ = true;
314 this->requested_ = params;
315 return this->advance_();
316}
317
319 if (this->scan_wanted_) {
320 // A stamp inherited from a stuck restart would fail the stop on its
321 // first advance.
323 }
324 this->scan_wanted_ = false;
325 this->advance_();
326}
327
329 // millis() on both sides: the loop clock is frozen while this blocks.
330 const uint32_t start = millis();
331 while (!this->scan_wanted_ && this->last_result_ == ScanOpResult::PENDING) {
332 if (millis() - start >= timeout_ms)
333 return false;
334 delay(RECONCILE_RETRY_MS);
335 this->advance_();
336 }
337 return this->last_result_ == ScanOpResult::SETTLED;
338}
339
340// Teardown is asynchronous: the handle is kept until an IDLE observation
341// confirms the radio is idle. A rejection WARNs once per failure streak and
342// widens the pump gate; the epilogue owns the stuck-teardown deadline.
344 const BdkOpResult result =
346 if (result == BdkOpResult::OK) {
347 this->release_warned_ = false;
348 return;
349 }
350 if (!this->release_warned_) {
351 // A hard error carries its code immediately; the 30 s stuck ERROR follows
352 // if it persists.
353 if (result == BdkOpResult::FAILED) {
354 ESP_LOGW(TAG, "Scan activity release failed (err %d); retrying", this->last_release_err_);
355 } else {
356 ESP_LOGW(TAG, "Scan activity release rejected; retrying");
357 }
358 this->release_warned_ = true;
359 }
360}
361
362// Stamp/track the teardown episode; once past the deadline, ERROR (re-logged
363// each interval) and report stuck.
365 if (this->teardown_since_ms_ == 0) {
366 this->teardown_since_ms_ = now;
367 this->teardown_stuck_log_ms_ = now; // first ERROR fires at the deadline
368 return false;
369 }
370 if (now - this->teardown_since_ms_ < TEARDOWN_STUCK_ERROR_MS)
371 return false;
372 if (now - this->teardown_stuck_log_ms_ >= TEARDOWN_STUCK_ERROR_MS) {
373 if (this->last_release_err_ != 0) {
374 ESP_LOGE(TAG, "Scan teardown cannot proceed; scanner is stuck (release err %d)", this->last_release_err_);
375 } else {
376 // No rejected release this episode: stuck waiting on the controller.
377 ESP_LOGE(TAG, "Scan teardown cannot proceed; scanner is stuck (controller busy)");
378 }
379 this->teardown_stuck_log_ms_ = now;
380 }
381 return true;
382}
383
384// One SDK operation per call toward the latched request; controller state is
385// read live each time (it changes on the BLE task, so nothing is mirrored).
386// The epilogue owns all deadlines and episode bookkeeping.
389 // Nothing to do; also keeps SDK reads off the pre-enable() path.
392 }
394 const bool ready = bdk_scan_ready();
395 ScanOpResult result = this->scan_wanted_ ? this->advance_start_(state, ready) : this->advance_stop_(state, ready);
396
398 this->last_advance_ms_ = now;
399 if (result == ScanOpResult::SETTLED || (state == BdkActivityState::IDLE && ready)) {
400 // Any teardown episode is over (IDLE observed with the controller
401 // settled, or e.g. a mode flip that settled back without ever reaching
402 // IDLE). An IDLE read while an operation is in flight proves nothing —
403 // a stop deferred there must keep its episode running.
405 this->release_warned_ = false;
406 }
408 // The mode-change release is observed complete; the rest is a normal
409 // bring-up on a fresh budget.
410 this->restarting_ = false;
411 this->pending_since_ms_ = now;
412 }
413 // Not chained to the clear above: a bring-up waiting at IDLE (create still
414 // in flight) must keep spending its budget.
415 if (result == ScanOpResult::PENDING) {
416 if (this->scan_wanted_ && state != BdkActivityState::STARTED && !this->restarting_) {
417 // A downed radio spends the bring-up budget; exhausting it hands
418 // recovery to the tracker's backoff.
419 if (now - this->pending_since_ms_ >= RECONCILE_PENDING_TIMEOUT_MS) {
420 ESP_LOGE(TAG, "Scan bring-up did not settle; giving up until the next start");
421 result = ScanOpResult::FAILED;
422 }
423 } else {
424 // A teardown is pending: a stop, or a mode-change release still in
425 // flight (restarting_); either way the bring-up budget waits.
426 if (this->scan_wanted_)
427 this->pending_since_ms_ = now;
428 if (this->teardown_stuck_(now)) {
429 // Terminal for stop AND restart: the tracker's backoff owns recovery
430 // (a stop's release keeps re-driving from loop(); a restart is
431 // re-requested through scan_start() with a fresh deadline).
432 result = ScanOpResult::FAILED;
433 }
434 }
435 }
436 this->last_result_ = result;
437 return result;
438}
439
441 if (state == BdkActivityState::IDLE && ready) {
442 // Fully torn down (or never created): the radio is idle. IDLE is trusted
443 // only when the controller is settled — mid-create the slot still reads
444 // IDLE, and dropping the handle then would leak the activity once the
445 // create lands.
448 }
449 if (!ready) {
450 // Acting mid-operation could delete an activity whose start lands
451 // afterwards, leaking the slot with the radio on; wait.
453 ESP_LOGD(TAG, "Scan stop deferred (controller busy)");
455 }
456 // Settled, so CREATED unambiguously means "never started".
457 this->release_activity_(state);
458 return ScanOpResult::PENDING; // confirmed once IDLE is observed
459}
460
463 if (this->applied_ == this->requested_)
465 // Running with different mode or parameters: tear down (the SDK stop
466 // chain also deletes the activity) and recreate on a later advance.
467 if (ready) {
468 this->release_activity_(state);
469 // Invalidate so a flip back to the old params cannot SETTLE against the
470 // activity being deleted (interval 0 never matches a real request).
471 this->applied_.interval = 0;
472 this->restarting_ = true;
473 }
475 }
476 if (!ready) {
478 ESP_LOGD(TAG, "Scan start deferred (controller busy)");
480 }
482 // Fire-and-forget: SETTLED only once a later advance observes the scan
483 // running, so a rejected start is retried rather than silently dead. On
484 // failure the created activity is intact; keep the handle.
485 if (bdk_scan_start(this->scan_activity_idx_, this->requested_.interval, this->requested_.window,
486 this->requested_.active) != BdkOpResult::OK)
488 this->applied_ = this->requested_;
490 }
491 if (state == BdkActivityState::OTHER)
492 return ScanOpResult::PENDING; // transitional; settles on a later read
493
494 // IDLE and ready: acquire a slot and create. A kept index is deliberately
495 // reused: SDK delete returns the slot to idle and create requires an idle
496 // slot, so it equals a fresh acquire — while clearing here would orphan a
497 // create still in flight (the BUSY race below).
502 }
503 switch (bdk_scan_create(this->scan_activity_idx_)) {
504 case BdkOpResult::BUSY: // raced the BLE task; keep the index, the retry resumes this slot
505 case BdkOpResult::OK:
508 break;
509 }
510 // Safe to clear (unlike BUSY): acquire is a pure search, so a rejected
511 // create leaves the slot IDLE for re-acquire.
514}
515
516} // namespace esphome::bk72xx_ble
517
518#endif // BK72XX_BLE_NO_SDK
519#endif // USE_BK72XX_BLE
struct bd_addr common_default_bdaddr
void ble_entry(void)
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
ScanOpResult scan_start(uint16_t interval, uint16_t window, bool active)
Request a scan (interval/window in 0.625 ms BLE units); enables the stack first if needed.
uint8_t ble_mac_[MAC_ADDRESS_SIZE]
Definition bk72xx_ble.h:144
float get_setup_priority() const override
ScanOpResult advance_stop_(BdkActivityState state, bool ready)
void release_activity_(BdkActivityState state)
StaticVector< BLEScanListener *, BK72XX_BLE_SCAN_LISTENER_COUNT > scan_listeners_
Definition bk72xx_ble.h:126
void enable()
Bring up the BDK BLE stack (one-time; the BDK has no teardown path).
esphome::LockFreeQueue< BLEScanReport, MAX_SCAN_REPORT_QUEUE_SIZE > report_queue_
Definition bk72xx_ble.h:131
void enqueue_scan_report(const uint8_t *mac, int8_t rssi, uint8_t addr_type, uint8_t evt_type, const uint8_t *data, uint16_t data_len)
Internal: buffer one controller report (BDK notice callback, BLE task context — bounded copy under th...
void get_mac_lsb_first(uint8_t out[MAC_ADDRESS_SIZE]) const
Controller BLE address, least-significant octet first (BLE convention).
ScanOpResult advance_start_(BdkActivityState state, bool ready)
esphome::EventPool< BLEScanReport, MAX_SCAN_REPORT_QUEUE_SIZE - 1 > report_pool_
Definition bk72xx_ble.h:135
bool teardown_stuck_(uint32_t now)
bool flush_pending_stop(uint32_t timeout_ms)
Drive a requested stop until the radio is observed idle, bounded by timeout_ms (for OTA).
void scan_stop()
Request the scanner stopped and the activity released; steps that cannot run yet are completed from l...
bool state
Definition fan.h:2
BdkOpResult bdk_scan_create(uint8_t activity_idx)
Create the scan activity (asynchronous); started once CREATED is observed.
Definition bdk_scan.cpp:70
BdkOpResult bdk_scan_release(uint8_t activity_idx, bool created, int *err_out)
Release the activity: delete when never started (a stop would be rejected), stop otherwise.
Definition bdk_scan.cpp:105
ScanOpResult
Outcome of one reconciliation step.
Definition bk72xx_ble.h:25
@ SETTLED
The request is reached: scan observed running, or stopped with the activity fully released.
@ FAILED
The controller rejected a step; retry later.
@ PENDING
A step is in flight; loop() keeps advancing — call scan_start() again to learn the outcome.
constexpr uint8_t INVALID_ACTIVITY_IDX
Activity index value marking "no scan activity", the BDK's own convention (asserted against its symbo...
Definition bdk_scan.h:13
uint8_t bdk_scan_acquire_activity()
Claim an idle activity slot; INVALID_ACTIVITY_IDX when none is free.
Definition bdk_scan.cpp:63
BdkOpResult bdk_scan_start(uint8_t activity_idx, uint16_t interval, uint16_t window, bool active)
Start a created activity: the packed GAPM start, taking the scan mode the BDK's own start path hardco...
Definition bdk_scan.cpp:80
BdkOpResult
Outcome of a BDK scan operation request.
Definition bdk_scan.h:24
@ BUSY
Another controller operation is in flight; retry later.
@ OK
Accepted; completion is asynchronous.
BdkActivityState
Scan-relevant controller activity states, read live from the SDK.
Definition bdk_scan.h:16
@ OTHER
A non-scan or transitional state; settles on a later read.
@ CREATED
Created but not started.
@ IDLE
No activity (or one whose create failed).
BdkActivityState bdk_scan_state(uint8_t activity_idx)
Live state of the given activity; INVALID_ACTIVITY_IDX reads as IDLE.
Definition bdk_scan.cpp:48
bool bdk_scan_ready()
True when no controller operation is in flight (APP_BLE_READY).
Definition bdk_scan.cpp:46
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
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:28
Application App
Global storage of Application pointer - only one Application can exist.
uint32_t * scan_start
static void uint32_t
One advertisement report from the controller.
Definition bk72xx_ble.h:42
uint8_t mac[MAC_ADDRESS_SIZE]
Definition bk72xx_ble.h:43
One scan request: mode plus timing, in BLE units (0.625 ms).
Definition bk72xx_ble.h:34