Skip to content

feat(switch2_pro): Nintendo Switch 2 Pro Controller BLE emulation - #765

Open
finger563 wants to merge 15 commits into
mainfrom
feat/switch2-pro
Open

feat(switch2_pro): Nintendo Switch 2 Pro Controller BLE emulation#765
finger563 wants to merge 15 commits into
mainfrom
feat/switch2-pro

Conversation

@finger563

Copy link
Copy Markdown
Contributor

Warning

Do not merge until h2zero/esp-nimble-cpp#443 is merged and released.
This PR bumps the esp-nimble-cpp submodule to a branch commit that carries the new
NimBLEServer::registerServicesFirst() API (needed for the exact GATT handle layout).
Once #443 is released, the submodule pin moves to the released commit and the manifest
dependency is bumped. Opening now for review.

Summary

Adds a new switch2_pro component that emulates a Nintendo Switch 2 Pro Controller
over BLE
so a real Switch 2 console accepts it as a native controller — pairing,
encrypted link, continuous input streaming, reconnect, and wake-from-sleep. Built on
espp::BleGattServer (NimBLE); implements the reverse-engineered Nintendo custom GATT
interface (not HID-over-GATT) and the console's custom pairing handshake (not BLE SMP).

Status

  • ESP32-C6 (and the other open-NimBLE-controller chips — C61/C2/H2): fully working,
    verified against a real console. Pairs (full battery + correct icon), streams input
    lag-free at ~62 Hz, buttons/sticks register on "Test Input Devices", and reconnect +
    wake-from-sleep work without re-pairing.
  • ESP32-S3: builds and pairs, but does not yet stream input reliably. Its closed
    BTDM BLE controller degrades the encrypted stream under sustained notifications. This
    is documented as a known issue and is a fast-follow — see the component README.

What's included

  • The switch2_pro component: pairing crypto (known-answer verified) + LTK injection,
    the exact GATT handle layout, the full console init/command sequence, NVS bond
    persistence, continuous input streaming with real (mbuf-pool-based) backpressure, and
    a wake_console() API.
  • A small, additive ble_gatt_server enhancement: conn_params_update_callback.
  • esp-nimble-cpp submodule bump for registerServicesFirst() (see the warning above).
  • The opt-in SWITCH2_PRO_PATCH_NIMBLE_5MS controller patch (off by default; required
    only for reconnect/wake, since the console connects a bonded controller at a sub-spec
    5 ms interval) + a hardware-free smoke_test_5ms.py verifier.
  • Docs (doc/en/ble/switch2_pro*, Doxyfile), CI matrix entries (C6 + S3), example, and
    DESIGN/README.

Testing

  • On-hardware against a real Switch 2 (ESP32-C6): pairing, input, reconnect, wake — all
    working; input stays lag-free indefinitely at the console's 15 ms / ~62 Hz cadence.
  • The example builds clean for both esp32c6 and esp32s3. The 5 ms patch is off by
    default so CI builds do not mutate the IDF install.

Attribution / scope

Interoperability only — no Nintendo or Espressif binaries are included. The pairing
"authentication" relies on a published fixed key (a possession check, not per-device
attestation). Protocol reverse-engineering credit: the community, principally
ndeadly/switch2_controller_research; the NimBLE 5 ms patch technique is adapted (MIT)
from zhantss/ESP32-BLE5-NSController-Emulator.

🤖 Generated with Claude Code

finger563 and others added 13 commits August 12, 2026 15:54
…r BLE emulation

New component that emulates a Nintendo Switch 2 Pro Controller as a BLE
peripheral so a real Switch 2 accepts it as a native controller (and can be
woken from sleep). Unlike the original Switch (BT Classic HID), the Switch 2
uses a proprietary BLE GATT interface (not HID-over-GATT) and a custom pairing
handshake (not SMP), so this builds custom Nintendo GATT services directly on
espp::BleGattServer rather than hid_service/hid-rp.

This first milestone:
- custom GATT service tree (two proprietary services + input/command/response
  characteristics) with no-SMP security config
- Nintendo manufacturer-data advertising (+ wake-flag variant scaffolding)
- reverse-engineered pairing crypto (LTK = A1 ^ B1, B2 = AES-128-ECB via PSA
  Crypto), self-tested against a host-verified known-answer vector at init
- Pro Controller 2 input report (0x09) struct incl. the C / GL / GR buttons
- opt-in, target-gated NimBLE 5 ms connection-interval patch (tools/ + Kconfig),
  off by default and never mutating $IDF_PATH silently

Builds and links for ESP32-C6. Protocol facts from
ndeadly/switch2_controller_research; approach + NimBLE patch adapted (MIT) from
zhantss/ESP32-BLE5-NSController-Emulator. See DESIGN.md for milestones 2-4.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…estone 2)

The command channel now replies, completing the pairing handshake on the wire
and answering the console's init sequence — the part a real console needs to
accept the controller.

- send device->host responses on the matching notify characteristic (writes on
  0x0016 reply on 0x001e, writes on 0x0014 reply on 0x001a)
- full 0x15 pairing handshake replies with exact captured framing: exchange
  addresses (our BT address), exchange keys (fixed B1), confirm (B2 = AES-ECB),
  finalise ACK — bytes verified against ndeadly's captures
- command dispatch: flash/calibration reads (0x02) from a simulated flash,
  feature-select (0x0c) mask capture, firmware-info (0x10) canned reply,
  firmware-update (0x0d) ACK to suppress the update prompt, and header-only
  ACKs for init/LEDs/vibration/battery so the console's state machine advances
- simulated flash (switch2_pro_flash.hpp) with neutral stick calibration
  (placeholder; refine against a capture for exact stick behavior)

Builds and links for ESP32-C6. Uncertain-on-hardware bits (exchange-address
byte order, calibration contents, firmware-update suppression) are marked in
code for refinement against a real console. Next: input report streaming
(milestone 3, needs the 5 ms NimBLE patch) and wake-from-sleep (milestone 4).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…iring tests

Make the example flashable on ESP32-S3 (what's commonly on hand) and observable
enough to see how far pairing with a real Switch 2 gets, since S3 can't accept
the console's 5 ms interval and may drop mid/post-pairing.

- example defaults to esp32s3 (builds clean; the C6 GCC 15.2 attribute issue is
  RISC-V-only, so S3 is unaffected) with USB-Serial-JTAG console
- connect/disconnect callbacks log the negotiated connection interval,
  supervision timeout, and disconnect reason — the interval is the 5 ms
  diagnostic, the reason shows why a link dropped
- DEBUG-level hex trace of every command write (0x0014/0x0016) and response
  (0x001a/0x001e), so the 0x15 pairing exchange is visible on the monitor
- re-advertise on disconnect
- example README: step-by-step pairing test and what to expect on S3 vs C6

Note: S3 pairing may not complete/hold (5 ms limit); the trace shows how far it
gets, which is the useful signal. Full path remains C6/C61 + the NimBLE patch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rtisement

The controller wasn't visible to the console because the advertisement
overflowed the 31-byte legacy limit: flags (3) + name "Pro Controller" (16) +
manufacturer data (22) = 41 bytes, so NimBLE dropped/truncated it — and the
console filters discovery on the Nintendo manufacturer data, which was the part
getting lost.

Put flags + manufacturer data (25 bytes, fits) in the primary advertisement and
move the device name to the scan response. Flags now 0x06 (LE General Disc +
BR/EDR unsupported), matching the captured discovery advertisement. Log the
manufacturer-data fit and warn if it still doesn't.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The NS2 now discovers and connects (stable at a 15ms interval — so the 5ms
wall isn't hit during pairing on S3), but doesn't start the pairing exchange.
We were blind to what it does post-connect (only the two command chars were
logged).

Attach a tracing callback to every custom characteristic that logs reads,
writes (with hex), and notification subscribes at INFO. This shows whether the
console is subscribing to our response/input characteristics and what, if
anything, it writes — the data needed to find why pairing doesn't start.

Hex dump is INFO during bring-up (dial back later).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The console connects but does no value reads/writes/subscribes on our
characteristics — consistent with it doing ATT service discovery (invisible to
characteristic callbacks) and then declining to proceed.

- log the actual handle each service/characteristic landed on vs the real
  controller's handles (0x000a/0x000e/0x0014/0x0016/0x001a/0x001e). If
  BleGattServer's GAP/GATT/DeviceInfo/Battery services shifted ours off those
  handles, and the console keys off them, that's the cause.
- enable NimBLE host INFO logging (+ compile-in debug) so GAP/ATT activity
  (MTU, discovery, connection-parameter updates, subscribes) is visible at the
  stack level.

Diagnostic only; revert the log levels once understood.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Prior round's handle dump read 0x0000 because NimBLE assigns handles only after
the server starts; move it to after ble_gatt_server_.start(). Turn the NimBLE
host log to DEBUG for its tags only (esp_log_level_set, so ATT discovery/reads/
writes show without flooding other components). Log authentication_complete so
we can tell if the console is (unexpectedly) running BLE SMP.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The prior config set CONFIG_BT_NIMBLE_LOG_LEVEL to INFO, which compiles out
NimBLE's ATT/GATT-server DEBUG logs entirely — so the console's service
discovery / reads / writes never printed regardless of runtime level. Set it to
DEBUG and raise the runtime default level. Verified the generated sdkconfig now
has CONFIG_BT_NIMBLE_LOG_LEVEL=0 and CONFIG_LOG_DEFAULT_LEVEL=4.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The NS2 does app-level pairing (the 0x15 command exchange), not BLE SMP. Our
bonding=true config created BLE bonds; the console then used GATT caching over
the bond and skipped service discovery on reconnect, getting stuck after the
MTU exchange. Set bonding=false and clear any stored bonds at startup so the
console re-discovers our GATT cleanly on each connection.

Diagnostics from this round also confirmed our characteristics are shifted off
the real controller's handles (command at 0x0033 vs 0x0014) because
BleGattServer registers GAP/GATT/DeviceInfo/Battery first — tracked for the
likely raw-NimBLE GATT rework if the console keys off fixed handles.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…chive)

The C6 controller archives are GNU-format (long-name symbol/string tables);
macOS's BSD ar fails to extract them ("File exists" on the / and // members),
which silently broke the patcher on macOS. Prefer the RISC-V toolchain's
riscv32-esp-elf-ar (on PATH after the IDF export script), then llvm-ar, then ar;
add an --ar override.

Verified end-to-end on IDF 6.0.1: ble_ll_conn.c.o has exactly one 7.5ms-floor
pattern; patch -> 5ms -> restore round-trips cleanly. Confirms the 5 ms patch
applies on IDF 6.0.1, not just the 5.5.3 the reference used.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cesFirst

Points the esp-nimble-cpp submodule at esp-cpp's feat/register-services-first
(also fast-forwards it to upstream h2zero 2.5.0). This pulls in the new opt-in
NimBLEServer::registerServicesFirst() API that switch2_pro needs to place its
Nintendo services at the low attribute handles a real console addresses by
fixed handle.

Upstream PR: h2zero/esp-nimble-cpp#443. Do not merge this espp change until that
PR is merged and released; the pin will then move to a released commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Surface NimBLE's BLE_GAP_EVENT_CONN_UPDATE via a new optional
conn_params_update_callback on BleGattServer::Callbacks, forwarded from a
BleGattServerCallbacks::onConnParamsUpdate override. Lets applications observe
connection-parameter updates (interval/latency/timeout) as they complete.
Additive and optional; existing users are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…, docs, CI

Complete the switch2_pro component so a real Nintendo Switch 2 accepts it as a
native Pro Controller. Verified end-to-end on ESP32-C6: pairing, encrypted
link, continuous input streaming (~62 Hz, matching a real controller), reconnect,
and wake-from-sleep.

Highlights:
- Streaming model: set_input_report() stores latest state; a driver-owned task
  streams it (continuous by default, on-change fallback) with real backpressure
  keyed on the host mbuf pool (NimBLE NOTIFY_TX fires at handoff, not over-air,
  so it can't gate a backlog — the pool level can).
- Correct report/init: firmware-info identity, always-0x38 byte 0x0b, all-zero
  IMU motion block, feature-mask restore on reconnect, exact GATT handle layout
  via NimBLEServer::registerServicesFirst().
- Reconnect + wake: bonded reconnect and 0x81 wake advertisement (public
  wake_console() API); needs the opt-in 5 ms controller patch (off by default).
- Tooling: patch_nimble_5ms.py (C6/C61/C2/H2 NimBLE + S3/C3 BTDM) and
  hardware-free smoke_test_5ms.py verifier.
- Docs (doc/en/ble/switch2_pro*, Doxyfile), CI matrix (C6 + S3), example, and
  README/DESIGN updated. C6 is the supported target; ESP32-S3 builds and pairs
  but does not yet stream reliably (closed BTDM controller) — documented as a
  known issue / fast-follow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 3, 2026 15:46
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

✅Static analysis result - no issues found! ✅

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are several correctness/documentation mismatches and missing error-handling paths (paired-state semantics, pthread config error checking, PSA crypto status handling, and dependency/version alignment) that should be resolved before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds a new switch2_pro component to ESPP that emulates a Nintendo Switch 2 Pro Controller over BLE (custom GATT + custom pairing/crypto + continuous input streaming), plus a small ble_gatt_server enhancement to support the required GATT/connection-parameter behavior, along with docs and CI coverage for the new example.

Changes:

  • Introduces the components/switch2_pro component (protocol constants, pairing crypto/self-test, GATT layout, advertising variants, bond persistence, and an input streaming task with backpressure).
  • Extends espp::BleGattServer with a connection-parameters-update callback and an option to disable built-in DIS/BAS services (needed for strict handle layout emulation).
  • Adds documentation pages, Doxygen inputs, and CI build matrix entries for the new example (ESP32-C6 + ESP32-S3).
File summaries
File Description
doc/en/ble/switch2_pro.rst New Sphinx page documenting the Switch 2 Pro BLE emulation component and 5 ms interval patch.
doc/en/ble/switch2_pro_example.md Includes the example README into the docs site.
doc/en/ble/index.rst Adds switch2_pro docs pages to the BLE docs toctree.
doc/Doxyfile Adds Switch2Pro headers and example to Doxygen INPUT/EXAMPLE_PATH.
components/switch2_pro/tools/smoke_test_5ms.py Hardware-free verifier for the 5 ms controller patch (disassembly-based).
components/switch2_pro/tools/patch_nimble_5ms.py Opt-in patcher that edits ESP-IDF controller archives to accept 5 ms intervals.
components/switch2_pro/src/switch2_pro.cpp Core implementation: GATT layout, pairing handling, bond persistence, advertising, and input streaming/backpressure.
components/switch2_pro/src/switch2_pro_pairing.cpp Pairing crypto implementation using PSA Crypto API + self-test.
components/switch2_pro/README.md Component README covering usage, 5 ms patch rationale, and known issues.
components/switch2_pro/Kconfig Adds SWITCH2_PRO_PATCH_NIMBLE_5MS opt-in configuration.
components/switch2_pro/include/switch2_pro.hpp Public API for espp::Switch2Pro and its configuration/streaming behavior.
components/switch2_pro/include/switch2_pro_report.hpp Defines the packed Pro Controller 2 input report helper.
components/switch2_pro/include/switch2_pro_protocol.hpp Protocol constants: UUIDs, command IDs, feature bits, manufacturer data, golden vectors.
components/switch2_pro/include/switch2_pro_pairing.hpp Pairing crypto interface and self-test API.
components/switch2_pro/include/switch2_pro_motion.hpp Embedded captured IMU motion sequence for optional replay.
components/switch2_pro/include/switch2_pro_flash.hpp Embedded captured flash blocks and a simulated flash reader.
components/switch2_pro/idf_component.yml Component Manager manifest for switch2_pro.
components/switch2_pro/example/sdkconfig.defaults.esp32c6 ESP32-C6-specific sdkconfig defaults (libc/toolchain workaround notes).
components/switch2_pro/example/sdkconfig.defaults Example defaults for BLE/NimBLE tuning and patch guidance.
components/switch2_pro/example/README.md Example walkthrough (pairing, streaming, wake/reconnect instructions).
components/switch2_pro/example/partitions.csv Partition table enabling NVS for bond persistence.
components/switch2_pro/example/main/switch2_pro_example.cpp Example app implementing BOOT-as-A and wake-on-press logic.
components/switch2_pro/example/main/CMakeLists.txt Registers the example main component.
components/switch2_pro/example/CMakeLists.txt Example project wiring for ESP-IDF build and components list.
components/switch2_pro/DESIGN.md Design notes, protocol sources/attribution, and patch rationale.
components/switch2_pro/CMakeLists.txt Component build registration + opt-in patch invocation at configure time.
components/switch2_pro/.gitignore Ignores example build artifacts and generated sdkconfig files.
components/ble_gatt_server/src/ble_gatt_server_callbacks.cpp Plumbs NimBLE conn-param-update event into server callbacks.
components/ble_gatt_server/include/ble_gatt_server.hpp Adds conn-param-update callback typedef + togglable DIS/BAS creation/start/deinit.
components/ble_gatt_server/include/ble_gatt_server_callbacks.hpp Declares the new onConnParamsUpdate callback override.
.github/workflows/build.yml Adds CI build matrix entries for switch2_pro/example (C6 + S3).
Review details
  • Files reviewed: 32/32 changed files
  • Comments generated: 6
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread components/switch2_pro/idf_component.yml
Comment thread components/switch2_pro/src/switch2_pro.cpp
Comment thread components/switch2_pro/src/switch2_pro.cpp Outdated
Comment thread components/switch2_pro/src/switch2_pro_pairing.cpp
Comment thread components/switch2_pro/include/switch2_pro.hpp Outdated
Comment thread components/switch2_pro/include/switch2_pro.hpp Outdated
- confirm(): check psa_crypto_init()/psa_cipher_encrypt() status and out_len;
  log and return zeroed output on failure instead of a silent partial block.
- init(): check esp_pthread_set_cfg() and warn if the streaming thread can't get
  its configured stack/prio/core.
- disconnect: stop clearing paired_ — is_paired() reports bond/handshake existence
  which survives a disconnect (use is_connected()/is_input_streaming() for session
  state).
- flash read: use std::find_if instead of a raw block-search loop (cppcheck).
- docs: refresh the class-level status comment (no longer a "skeleton"); fix the
  send_ack() doc to match the on-air bytes (0x10/0x78, no payload).
- manifest: note the unreleased esp-nimble-cpp registerServicesFirst() dependency.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical synchronization, lifecycle, handshake, and configuration issues remain unresolved.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (13)

Previously missed (12) — in code that hasn't changed since the last review.

components/switch2_pro/example/main/switch2_pro_example.cpp:31

  • Initialization failures are ignored, including failure of the erase/retry path, so the example proceeds even though this component's advertised reconnect/wake behavior cannot persist a bond. Check the final NVS status before constructing the controller.
    components/switch2_pro/include/switch2_pro_report.hpp:82
  • At the documented neutral input v == 0, this expression truncates 4095 / 2 to 2047, despite STICK_CENTER being 2048. Consequently every default/reset report encodes both sticks one count off center. Scale each half around STICK_CENTER so the endpoints remain 0/4095 and zero maps exactly to 2048.
    components/switch2_pro/src/switch2_pro.cpp:425
  • The underlying API returns false when advertising cannot start, but that result is discarded and success is logged; wake_console() likewise returns true unconditionally. This makes callers believe the wake/discovery advertisement was issued when it was not. Return and propagate the start result through start_advertising(), advertise(), wake_console(), and initialization.
    components/switch2_pro/src/switch2_pro.cpp:445
  • wake_console_on_boot_ is immutable and remains true after the first successful connection. With the default configuration, every later disconnect therefore immediately advertises the wake variant, and the persistent timer resumes doing so every five seconds, potentially waking a console the user just put to sleep. Use a one-shot wake-pending latch initialized at boot and clear/cancel it on successful connection, as the public configuration contract states.
    components/switch2_pro/src/switch2_pro.cpp:788
  • Byte 0x0b is forced to the rumble-enabled value even when FEATURE_SELECT has not enabled rumble or has explicitly disabled it. This contradicts Pro2InputReport::reset() and the feature-state contract in the header, and sends a feature state different from the negotiated mask.
    components/switch2_pro/src/switch2_pro.cpp:853
  • A failed ble_store_write_our_sec is logged at INFO as “ready,” and callers continue to mark/save the pairing even though the controller cannot answer the upcoming encryption request. Propagate this failure from inject_ltk() so finalization/reconnect can fail explicitly rather than reporting a usable bond.
    components/switch2_pro/src/switch2_pro.cpp:885
  • The return values from nvs_set_blob and nvs_commit are ignored, yet the method logs success and updates the in-memory bond even when persistence failed. This makes pairing appear durable while reconnect/wake fails after reboot. Only publish success after both operations complete successfully.
    components/switch2_pro/tools/patch_nimble_5ms.py:205
  • Targets with two controller archives are modified one at a time. If the first archive is patched and validation of the second archive fails, the tool exits after leaving the global ESP-IDF installation partially patched. Preflight every archive and create all backups before writing any archive, or roll back already-written archives on failure.
    components/switch2_pro/tools/smoke_test_5ms.py:99
  • The smoke test documents exit code 2 for tool errors, but failures from ar raise CalledProcessError, missing tools raise OSError, and objdump failures are silently treated as empty output; the first two terminate with Python's exit code 1, which is documented as “unpatched.” Catch tool-execution failures and consistently return the indeterminate/error status 2.
    components/switch2_pro/DESIGN.md:43
  • This states that every link is driven at 5 ms and cannot stream without the patch, contradicting the verified behavior documented elsewhere in this PR: fresh pairing and first-session streaming use 15 ms, while only bonded reconnect/wake starts at 5 ms. Clarify the distinction here because it changes whether the invasive patch is required.
    components/switch2_pro/include/switch2_pro.hpp:195
  • This says on-change mode is the default, but Config::continuous_streaming defaults to true. Update the method documentation so generated API docs describe the actual default behavior.
    components/switch2_pro/include/switch2_pro_protocol.hpp:40
  • The protocol comment declares this characteristic as WRITE, but build_gatt() intentionally creates it with NIMBLE_PROPERTY::WRITE_NR to match the real controller. Correct the public protocol documentation to avoid sending users toward the wrong GATT layout.

components/switch2_pro/DESIGN.md:85

  • This says absolute handles are not strict and “we discover by UUID,” whereas the implementation and required registerServicesFirst() API are explicitly based on the console using fixed handles without discovery. Resolve this contradiction; it changes whether the exact GATT ordering is a protocol requirement or merely emulation fidelity.
Two proprietary primary services; contiguous handles matter for some console
firmwares (FW 2.0.0+ shifts them +8 for headset audio, so absolute-handle dependence
is not strict — we reproduce the map but discover by UUID).
  • Files reviewed: 32/32 changed files
  • Comments generated: 9
  • Review effort level: Balanced

Comment thread components/switch2_pro/include/switch2_pro.hpp Outdated
Comment thread components/switch2_pro/src/switch2_pro.cpp
Comment thread components/switch2_pro/src/switch2_pro.cpp
Comment thread components/switch2_pro/src/switch2_pro.cpp
Comment thread components/switch2_pro/src/switch2_pro.cpp
Comment thread components/switch2_pro/CMakeLists.txt
Comment thread .github/workflows/build.yml Outdated
Comment thread doc/Doxyfile Outdated
Comment thread doc/Doxyfile Outdated
- data races: make the cross-thread session state atomic (input_subscribed_,
  active_conn_handle_, enabled_features_) and move the grouped per-session
  counter/link-baseline resets out of on_subscribe into the streaming thread's
  session-start, so they stay single-writer.
- lifecycle: in ~Switch2Pro() stop/join the wake timer and detach the
  this-capturing GAP/GATT callbacks (+ stop advertising) before members are
  destroyed, so a late callback can't touch freed state.
- pairing: gate FINALISE on an in-order handshake (pairing_stage_ must reach 3:
  address-exchange -> key-exchange -> confirm) so a malformed/out-of-order peer
  can't persist an all-zero/partial bond.
- portability: #error if CONFIG_BT_NIMBLE_EXT_ADV is enabled (this component uses
  the legacy advertising path).
- register the component in upload_components.yml; fix alphabetical ordering in
  build.yml and Doxyfile (switch2_pro before sx126x; headers main/protocol/report).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants