ESPHome 2026.10.0-dev
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 }
187#endif
188
190 ESP_LOGD(TAG, "BLE stack initialised");
191}
192
194 // Keep reconciling toward the requested scan state (e.g. complete a stop
195 // that arrived while a controller operation was in flight), and re-check a
196 // settled scan at low frequency: a controller-side drop re-enters the
197 // bring-up, and the budget's FAILED feeds the tracker's recovery.
198 // Keep driving until settled: any PENDING, plus a terminal stop whose slot
199 // must still be freed. A FAILED scan request is the one combination not
200 // re-driven here — that belongs to the tracker's backoff.
201 const uint32_t pump_now = App.get_loop_component_start_time();
202 if (this->last_result_ == ScanOpResult::PENDING ||
203 (!this->scan_wanted_ && this->last_result_ == ScanOpResult::FAILED)) {
204 const uint32_t gate = (this->release_warned_ || this->last_result_ == ScanOpResult::FAILED)
205 ? RECONCILE_REJECTED_RETRY_MS
206 : RECONCILE_RETRY_MS;
207 if (pump_now - this->last_advance_ms_ >= gate)
208 this->advance_();
209 } else if (this->scan_wanted_ && this->last_result_ == ScanOpResult::SETTLED &&
210 pump_now - this->last_advance_ms_ >= SCAN_LIVENESS_CHECK_MS) {
211 // Re-check a settled scan; scan_start() refills the bring-up budget.
212 // WARN: the only report of a drop that recovers inside its budget.
213 if (this->scan_start(this->requested_.interval, this->requested_.window, this->requested_.active) !=
215 ESP_LOGW(TAG, "Controller dropped the scan; restarting");
216 }
217 }
218
219 // Drain the lock-free ring filled by the BLE task; all per-report work runs
220 // here on the main task, then the report returns to the pool.
221 BLEScanReport *report = this->report_queue_.pop();
222 if (report == nullptr)
223 return;
224 do {
225#ifdef BK72XX_BLE_SCAN_LISTENER_COUNT
226 for (auto *listener : this->scan_listeners_)
227 listener->on_scan_report(*report);
228#endif
229 this->report_pool_.release(report);
230 } while ((report = this->report_queue_.pop()) != nullptr);
231
232 // Log dropped reports — only reachable when reports were processed; drops can
233 // only occur while the queue is full, and only this loop drains it.
234 uint16_t dropped = this->report_queue_.get_and_reset_dropped_count();
235 if (dropped > 0) {
236 ESP_LOGW(TAG, "Dropped %u scan reports due to queue overflow", dropped);
237 }
238}
239
240void BK72xxBLE::get_mac_lsb_first(uint8_t out[MAC_ADDRESS_SIZE]) const {
241 for (int i = 0; i < 6; i++)
242 out[i] = this->ble_mac_[i];
243}
244
246 // ble_mac_ is stored LSB-first (BLE convention); print [5..0] for the
247 // MSB-first order Home Assistant shows.
248 ESP_LOGCONFIG(TAG,
249 "BK72xx BLE:\n"
250 " MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n"
251 " Active: %s",
252 this->ble_mac_[5], this->ble_mac_[4], this->ble_mac_[3], this->ble_mac_[2], this->ble_mac_[1],
253 this->ble_mac_[0], YESNO(this->is_active()));
254}
255
256// ---------------------------------------------------------------------------
257// MAC resolution
258// ---------------------------------------------------------------------------
259
261#ifdef BK72XX_BLE_HAS_COMMON_BDADDR
262 // BK7231N: the BDK populates common_default_bdaddr (LSB-first, BLE convention)
263 // during ble_entry(). It may still be zero before the stack is up; if so, fall
264 // through to the WiFi-derived MAC below.
265 bool nonzero = false;
266 for (uint8_t b : common_default_bdaddr.addr) {
267 if (b != 0) {
268 nonzero = true;
269 break;
270 }
271 }
272 if (nonzero) {
273 memcpy(this->ble_mac_, common_default_bdaddr.addr, MAC_ADDRESS_SIZE);
274 return;
275 }
276#endif
277 // Chips whose BLE stack does not export common_default_bdaddr (BK7238 and the other
278 // BLE-5.x SoCs), or BK7231N before the stack is up: derive the BLE MAC exactly as the
279 // Beken BDK does in bdaddr_env_init() — the WiFi STA MAC with only its last byte
280 // incremented (sta_mac[5] += 1, a plain byte increment with no carry into the next
281 // byte), OUI unchanged. This reproduces the address the controller advertises with
282 // (verified against the BK7231N BLE-5.1 and BK7252N/BK7238 BLE-5.2 SDK sources), so it
283 // matches on every device, including the last-byte == 0xFF edge that a 24-bit increment
284 // would carry differently.
285 uint8_t wifi_mac[MAC_ADDRESS_SIZE];
286 get_mac_address_raw(wifi_mac); // MSB-first
287 const uint8_t ble[MAC_ADDRESS_SIZE] = {wifi_mac[0], wifi_mac[1], wifi_mac[2],
288 wifi_mac[3], wifi_mac[4], static_cast<uint8_t>(wifi_mac[5] + 1)};
289 // Store LSB-first to match recv_adv_t adv_addr ordering.
290 for (int i = 0; i < 6; i++)
291 this->ble_mac_[i] = ble[5 - i];
292}
293
294// ---------------------------------------------------------------------------
295// Scan reconciler
296// ---------------------------------------------------------------------------
297
298// Episode boundary: fresh teardown deadline and error bookkeeping.
300 this->teardown_since_ms_ = 0;
301 this->restarting_ = false;
302 this->last_release_err_ = 0;
303}
304
305ScanOpResult BK72xxBLE::scan_start(uint16_t interval, uint16_t window, bool active) {
306 if (!this->is_active())
307 this->enable();
308
309 const ScanParams params{active, interval, window};
310 // A new episode refills the budget and gets a fresh teardown deadline; a
311 // re-call observing an in-flight bring-up (last result PENDING) must not.
312 if (this->last_result_ != ScanOpResult::PENDING || !this->scan_wanted_ || params != this->requested_) {
315 }
316 this->scan_wanted_ = true;
317 this->requested_ = params;
318 return this->advance_();
319}
320
322 if (this->scan_wanted_) {
323 // A stamp inherited from a stuck restart would fail the stop on its
324 // first advance.
326 }
327 this->scan_wanted_ = false;
328 this->advance_();
329}
330
332 // millis() on both sides: the loop clock is frozen while this blocks.
333 const uint32_t start = millis();
334 while (!this->scan_wanted_ && this->last_result_ == ScanOpResult::PENDING) {
335 if (millis() - start >= timeout_ms)
336 return false;
337 delay(RECONCILE_RETRY_MS);
338 this->advance_();
339 }
340 return this->last_result_ == ScanOpResult::SETTLED;
341}
342
343// Teardown is asynchronous: the handle is kept until an IDLE observation
344// confirms the radio is idle. A rejection WARNs once per failure streak and
345// widens the pump gate; the epilogue owns the stuck-teardown deadline.
347 const BdkOpResult result =
349 if (result == BdkOpResult::OK) {
350 this->release_warned_ = false;
351 return;
352 }
353 if (!this->release_warned_) {
354 // A hard error carries its code immediately; the 30 s stuck ERROR follows
355 // if it persists.
356 if (result == BdkOpResult::FAILED) {
357 ESP_LOGW(TAG, "Scan activity release failed (err %d); retrying", this->last_release_err_);
358 } else {
359 ESP_LOGW(TAG, "Scan activity release rejected; retrying");
360 }
361 this->release_warned_ = true;
362 }
363}
364
365// Stamp/track the teardown episode; once past the deadline, ERROR (re-logged
366// each interval) and report stuck.
368 if (this->teardown_since_ms_ == 0) {
369 this->teardown_since_ms_ = now;
370 this->teardown_stuck_log_ms_ = now; // first ERROR fires at the deadline
371 return false;
372 }
373 if (now - this->teardown_since_ms_ < TEARDOWN_STUCK_ERROR_MS)
374 return false;
375 if (now - this->teardown_stuck_log_ms_ >= TEARDOWN_STUCK_ERROR_MS) {
376 if (this->last_release_err_ != 0) {
377 ESP_LOGE(TAG, "Scan teardown cannot proceed; scanner is stuck (release err %d)", this->last_release_err_);
378 } else {
379 // No rejected release this episode: stuck waiting on the controller.
380 ESP_LOGE(TAG, "Scan teardown cannot proceed; scanner is stuck (controller busy)");
381 }
382 this->teardown_stuck_log_ms_ = now;
383 }
384 return true;
385}
386
387// One SDK operation per call toward the latched request; controller state is
388// read live each time (it changes on the BLE task, so nothing is mirrored).
389// The epilogue owns all deadlines and episode bookkeeping.
392 // Nothing to do; also keeps SDK reads off the pre-enable() path.
395 }
397 const bool ready = bdk_scan_ready();
398 ScanOpResult result = this->scan_wanted_ ? this->advance_start_(state, ready) : this->advance_stop_(state, ready);
399
401 this->last_advance_ms_ = now;
402 if (result == ScanOpResult::SETTLED || (state == BdkActivityState::IDLE && ready)) {
403 // Any teardown episode is over (IDLE observed with the controller
404 // settled, or e.g. a mode flip that settled back without ever reaching
405 // IDLE). An IDLE read while an operation is in flight proves nothing —
406 // a stop deferred there must keep its episode running.
408 this->release_warned_ = false;
409 }
411 // The mode-change release is observed complete; the rest is a normal
412 // bring-up on a fresh budget.
413 this->restarting_ = false;
414 this->pending_since_ms_ = now;
415 }
416 // Not chained to the clear above: a bring-up waiting at IDLE (create still
417 // in flight) must keep spending its budget.
418 if (result == ScanOpResult::PENDING) {
419 if (this->scan_wanted_ && state != BdkActivityState::STARTED && !this->restarting_) {
420 // A downed radio spends the bring-up budget; exhausting it hands
421 // recovery to the tracker's backoff.
422 if (now - this->pending_since_ms_ >= RECONCILE_PENDING_TIMEOUT_MS) {
423 ESP_LOGE(TAG, "Scan bring-up did not settle; giving up until the next start");
424 result = ScanOpResult::FAILED;
425 }
426 } else {
427 // A teardown is pending: a stop, or a mode-change release still in
428 // flight (restarting_); either way the bring-up budget waits.
429 if (this->scan_wanted_)
430 this->pending_since_ms_ = now;
431 if (this->teardown_stuck_(now)) {
432 // Terminal for stop AND restart: the tracker's backoff owns recovery
433 // (a stop's release keeps re-driving from loop(); a restart is
434 // re-requested through scan_start() with a fresh deadline).
435 result = ScanOpResult::FAILED;
436 }
437 }
438 }
439 this->last_result_ = result;
440 return result;
441}
442
444 if (state == BdkActivityState::IDLE && ready) {
445 // Fully torn down (or never created): the radio is idle. IDLE is trusted
446 // only when the controller is settled — mid-create the slot still reads
447 // IDLE, and dropping the handle then would leak the activity once the
448 // create lands.
451 }
452 if (!ready) {
453 // Acting mid-operation could delete an activity whose start lands
454 // afterwards, leaking the slot with the radio on; wait.
455 if (this->last_result_ == ScanOpResult::SETTLED) {
456 ESP_LOGD(TAG, "Scan stop deferred (controller busy)");
457 }
459 }
460 // Settled, so CREATED unambiguously means "never started".
461 this->release_activity_(state);
462 return ScanOpResult::PENDING; // confirmed once IDLE is observed
463}
464
467 if (this->applied_ == this->requested_)
469 // Running with different mode or parameters: tear down (the SDK stop
470 // chain also deletes the activity) and recreate on a later advance.
471 if (ready) {
472 this->release_activity_(state);
473 // Invalidate so a flip back to the old params cannot SETTLE against the
474 // activity being deleted (interval 0 never matches a real request).
475 this->applied_.interval = 0;
476 this->restarting_ = true;
477 }
479 }
480 if (!ready) {
481 if (this->last_result_ == ScanOpResult::SETTLED) {
482 ESP_LOGD(TAG, "Scan start deferred (controller busy)");
483 }
485 }
487 // Fire-and-forget: SETTLED only once a later advance observes the scan
488 // running, so a rejected start is retried rather than silently dead. On
489 // failure the created activity is intact; keep the handle.
490 if (bdk_scan_start(this->scan_activity_idx_, this->requested_.interval, this->requested_.window,
491 this->requested_.active) != BdkOpResult::OK)
493 this->applied_ = this->requested_;
495 }
496 if (state == BdkActivityState::OTHER)
497 return ScanOpResult::PENDING; // transitional; settles on a later read
498
499 // IDLE and ready: acquire a slot and create. A kept index is deliberately
500 // reused: SDK delete returns the slot to idle and create requires an idle
501 // slot, so it equals a fresh acquire — while clearing here would orphan a
502 // create still in flight (the BUSY race below).
507 }
508 switch (bdk_scan_create(this->scan_activity_idx_)) {
509 case BdkOpResult::BUSY: // raced the BLE task; keep the index, the retry resumes this slot
510 case BdkOpResult::OK:
513 break;
514 }
515 // Safe to clear (unlike BUSY): acquire is a pure search, so a rejected
516 // create leaves the slot IDLE for re-acquire.
519}
520
521} // namespace esphome::bk72xx_ble
522
523#endif // BK72XX_BLE_NO_SDK
524#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:71
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:106
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:81
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:87
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