mirror of
https://github.com/espressif/esp-idf.git
synced 2026-05-28 16:46:31 +03:00
Merge branch 'feat/share_ble_uart_component_and_refresh_docs' into 'master'
feat(bt): add shared ble_uart component and update ble_uart_service/docs See merge request espressif/esp-idf!48321
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
# CMakeLists in this exact order for cmake to work correctly.
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
# Shared `ble_uart` component (must be visible before `project()` so
|
||||
# `main` can `REQUIRES ble_uart`; path is relative to this example root).
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "${CMAKE_CURRENT_LIST_DIR}/../common/ble_uart")
|
||||
|
||||
cmake_path(CONVERT "$ENV{IDF_PATH}" TO_CMAKE_PATH_LIST ESP_IDF_PATH)
|
||||
list(APPEND sdkconfig_defaults "${ESP_IDF_PATH}/components/mbedtls/config/mbedtls_preset_bt.conf")
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | --------- |
|
||||
|
||||
A turnkey serial-over-BLE peripheral that implements the de-facto
|
||||
**Nordic UART Service** GATT layout (RX write, TX notify), so any
|
||||
standard BLE-serial central (nRF Connect, Web Bluetooth examples, your
|
||||
own iOS / Android / Linux / Python scripts) can talk to it unchanged.
|
||||
**BLE UART-over-GATT** layout (RX write, TX notify; fixed 128-bit UUIDs
|
||||
below), so any widely used BLE-serial central (mobile GATT client apps,
|
||||
Web Bluetooth examples, your own iOS / Android / Linux / Python scripts)
|
||||
can talk to it unchanged.
|
||||
|
||||
The example ships with **two interchangeable backends** — NimBLE and
|
||||
Bluedroid — both implementing the same stack-agnostic
|
||||
@@ -19,7 +20,7 @@ manager, advertising, pairing, GAP event handling — is wrapped behind
|
||||
**two function calls** in `app_main`:
|
||||
|
||||
```c
|
||||
ble_uart_install(&cfg); // NimBLE host + NUS GATT service
|
||||
ble_uart_install(&cfg); // NimBLE host + BLE UART GATT service
|
||||
ble_uart_open(); // start advertising + auto-encrypt
|
||||
```
|
||||
|
||||
@@ -53,12 +54,14 @@ The `_ENC | _AUTHEN` flags are turned on only when `cfg.encrypted = true`
|
||||
| File | Lines | Role |
|
||||
| --- | ---: | --- |
|
||||
| `main/main.c` | ~70 | NVS init, MAC-derived device name, install + open, RX echo handler. Identical for both backends. |
|
||||
| `main/ble_uart.h` | ~260 | Stack-agnostic public API: 3-field config + 4 lifecycle functions + TX/status + UUID + `BLE_UART_E*` return codes. No NimBLE / Bluedroid types leak through. |
|
||||
| `main/ble_uart_nimble.c` | ~670 | NimBLE backend: host bring-up, NUS GATT service via `ble_gatts_add_svcs`, advertising, pairing, install/open/close/uninstall. Active when `CONFIG_BT_NIMBLE_ENABLED=y`. |
|
||||
| `main/ble_uart_bluedroid.c` | ~900 | Bluedroid backend: controller + host enable, NUS GATT service via `esp_ble_gatts_create_attr_tab` (service-table API), advertising, pairing, full PREP/EXEC long-write reassembly, install/open/close/uninstall. Active when `CONFIG_BT_BLUEDROID_ENABLED=y`. |
|
||||
| `main/Kconfig.projbuild` | ~40 | Device-name prefix + RX scratch buffer size knobs. |
|
||||
| `CMakeLists.txt` (root) | ~15 | `list(APPEND EXTRA_COMPONENT_DIRS .../common/ble_uart)` before `project()` so `main` can `REQUIRES ble_uart`. |
|
||||
| `../common/ble_uart/ble_uart.h` | ~155 | Stack-agnostic public API: 3-field config + 4 lifecycle functions + TX/status + UUID + `BLE_UART_E*` return codes. No NimBLE / Bluedroid types leak through. |
|
||||
| `../common/ble_uart/ble_uart_nimble.c` | ~650 | NimBLE backend: host bring-up, BLE UART GATT service via `ble_gatts_add_svcs`, advertising, pairing, install/open/close/uninstall. Active when `CONFIG_BT_NIMBLE_ENABLED=y`. |
|
||||
| `../common/ble_uart/ble_uart_bluedroid.c` | ~1020 | Bluedroid backend: controller + host enable, BLE UART GATT service via `esp_ble_gatts_create_attr_tab` (service-table API), advertising, pairing, full PREP/EXEC long-write reassembly, install/open/close/uninstall. Active when `CONFIG_BT_BLUEDROID_ENABLED=y`. |
|
||||
| `../common/ble_uart/Kconfig` | ~30 | Device-name prefix + RX scratch size (`menuconfig → Component configuration → BLE UART library`). |
|
||||
| `../common/ble_uart/PORTING.md` | ~724 | Porting and API guide (integration, CMake, sdkconfig, thread safety). |
|
||||
| `sdkconfig.defaults` | — | Default: NimBLE backend, MTU 512, SC + bonding + persistent NVS. |
|
||||
| `sdkconfig.ci.bluedroid` | — | Overlay: switch to Bluedroid backend (used via `-D SDKCONFIG_DEFAULTS=...`, see "Choosing the host stack" below). |
|
||||
| `sdkconfig.bluedroid` | — | Overlay: switch to Bluedroid backend (used via `-D SDKCONFIG_DEFAULTS=...`, see "Choosing the host stack" below). |
|
||||
|
||||
## Public API
|
||||
|
||||
@@ -91,10 +94,10 @@ extern const ble_uart_uuid128_t ble_uart_service_uuid;
|
||||
|
||||
The same `ble_uart.h` API is implemented twice — once on top of NimBLE
|
||||
(`ble_uart_nimble.c`) and once on top of Bluedroid
|
||||
(`ble_uart_bluedroid.c`). `main/CMakeLists.txt` registers both files;
|
||||
each guards its body with `#if CONFIG_BT_NIMBLE_ENABLED` / `#if
|
||||
CONFIG_BT_BLUEDROID_ENABLED`, so exactly one becomes live at compile
|
||||
time.
|
||||
(`ble_uart_bluedroid.c`). The shared `ble_uart` component's
|
||||
`CMakeLists.txt` registers both files; each guards its body with
|
||||
`#if CONFIG_BT_NIMBLE_ENABLED` / `#if CONFIG_BT_BLUEDROID_ENABLED`, so
|
||||
exactly one becomes live at compile time.
|
||||
|
||||
Two ways to switch:
|
||||
|
||||
@@ -103,9 +106,9 @@ Two ways to switch:
|
||||
idf.py menuconfig
|
||||
# Component config -> Bluetooth -> Host -> NimBLE / Bluedroid
|
||||
|
||||
# B. Apply the Bluedroid overlay non-interactively (great for CI)
|
||||
# B. Apply the Bluedroid overlay non-interactively (scripts / reproducible builds)
|
||||
idf.py -B build_bd \
|
||||
-D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.ci.bluedroid" \
|
||||
-D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.bluedroid" \
|
||||
reconfigure
|
||||
idf.py -B build_bd build flash monitor
|
||||
```
|
||||
@@ -130,10 +133,16 @@ When neither is enabled the build fails up-front with a clear error.
|
||||
```bash
|
||||
idf.py set-target esp32c3 # or esp32, esp32s3, esp32c6, esp32h2 ...
|
||||
idf.py menuconfig # optional
|
||||
# Component config -> BLE UART Example
|
||||
# Component configuration -> BLE UART library
|
||||
# - BLE device name prefix (default: BleUart)
|
||||
# - RX scratch buffer size (default: 1024 bytes)
|
||||
```
|
||||
|
||||
Those `BLE_UART_*` options are defined in **`../common/ble_uart/Kconfig`**
|
||||
(the `ble_uart` component); they appear whenever `ble_uart` is part of the
|
||||
build (this example pulls it in via `EXTRA_COMPONENT_DIRS` in the root
|
||||
`CMakeLists.txt`).
|
||||
|
||||
The two security knobs are set in `sdkconfig.defaults`:
|
||||
|
||||
```ini
|
||||
@@ -152,7 +161,7 @@ idf.py build flash monitor
|
||||
```
|
||||
|
||||
Expected boot log (NimBLE backend — the per-characteristic register
|
||||
lines are NimBLE-specific; Bluedroid prints the four NUS handles in a
|
||||
lines are NimBLE-specific; Bluedroid prints the four UART-service handles in a
|
||||
single line, see below):
|
||||
|
||||
```
|
||||
@@ -174,7 +183,9 @@ I (xxx) ble_uart: advertising started
|
||||
|
||||
## Pairing & demo
|
||||
|
||||
1. On a phone, install **nRF Connect for Mobile**.
|
||||
1. On a phone, install **a BLE GATT client app** that supports scanning,
|
||||
pairing, characteristic write, and notify/CCCD (many mobile “BLE tools”
|
||||
or serial-over-BLE utilities qualify).
|
||||
2. Scan, tap **Connect** on `BleUart-XXXX`. The phone prompts for a
|
||||
6-digit code.
|
||||
3. The device prints a fresh code in a banner on UART:
|
||||
@@ -187,7 +198,7 @@ I (xxx) ble_uart: advertising started
|
||||
```
|
||||
4. Type that code on the phone; pairing completes. The link is now
|
||||
AES-CCM-encrypted and the LTK is stored to NVS.
|
||||
5. Open the *Nordic UART Service*, subscribe to TX (the down-arrow
|
||||
5. Open the **UART service** (UUID `6e400001-…`), subscribe to TX (the down-arrow
|
||||
icon), then write any bytes to RX (the up-arrow icon). The device
|
||||
logs them to UART and **echoes them right back** through TX.
|
||||
6. Disconnect and reconnect: no passkey prompt — the bond resumes
|
||||
@@ -204,9 +215,26 @@ bytes with no framing assumptions). Send replies with `ble_uart_tx()`.
|
||||
|
||||
## Reusing `ble_uart` in your own project
|
||||
|
||||
Copy `main/ble_uart.h` plus the backend(s) you want — `main/ble_uart_nimble.c`
|
||||
and/or `main/ble_uart_bluedroid.c` — into your project, add `bt nvs_flash`
|
||||
to your component's `REQUIRES`, then in your `app_main`:
|
||||
**Recommended (no copy):** register the shared component directory **before**
|
||||
`project()` so CMake can resolve `REQUIRES ble_uart` from `main/` (same pattern
|
||||
as this example's root `CMakeLists.txt`):
|
||||
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "${CMAKE_CURRENT_LIST_DIR}/../path/to/common/ble_uart")
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(my_app)
|
||||
```
|
||||
|
||||
Then in `main/CMakeLists.txt` use `REQUIRES ble_uart nvs_flash` and
|
||||
`#include "ble_uart.h"`.
|
||||
|
||||
Adjust the `EXTRA_COMPONENT_DIRS` path if you vendor `common/ble_uart` elsewhere
|
||||
(e.g. `${CMAKE_CURRENT_LIST_DIR}/components/ble_uart`).
|
||||
|
||||
**Alternative:** copy the whole `examples/bluetooth/common/ble_uart/` directory
|
||||
into your tree (or only the `.h` / `.c` files into `main/`) and add `bt nvs_flash`
|
||||
to that component's `REQUIRES`, then in your `app_main`:
|
||||
|
||||
```c
|
||||
nvs_flash_init();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
# Both backends are listed; each .c file body is wrapped in
|
||||
# #if CONFIG_BT_NIMBLE_ENABLED / CONFIG_BT_BLUEDROID_ENABLED so only
|
||||
# the matching backend produces code. This is the standard IDF
|
||||
# pattern for conditional sources, because sdkconfig isn't loaded
|
||||
# during the early CMake component-requirement scan.
|
||||
idf_component_register(SRCS "main.c"
|
||||
"ble_uart_nimble.c"
|
||||
"ble_uart_bluedroid.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES bt nvs_flash)
|
||||
REQUIRES ble_uart nvs_flash)
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
# from the default NimBLE backend to Bluedroid. Use it like:
|
||||
#
|
||||
# idf.py -B build_bd \
|
||||
# -D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.ci.bluedroid" \
|
||||
# -D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.bluedroid" \
|
||||
# reconfigure
|
||||
# idf.py -B build_bd build flash monitor
|
||||
#
|
||||
# When this overlay wins, main/CMakeLists.txt links ble_uart_bluedroid.c
|
||||
# instead of ble_uart_nimble.c. The public ble_uart.h API is identical
|
||||
# When this overlay wins, the ble_uart component compiles
|
||||
# ble_uart_bluedroid.c instead of ble_uart_nimble.c (each backend is
|
||||
# gated on CONFIG_BT_*_ENABLED). The public ble_uart.h API is identical
|
||||
# either way.
|
||||
|
||||
CONFIG_BT_ENABLED=y
|
||||
9
examples/bluetooth/common/ble_uart/CMakeLists.txt
Normal file
9
examples/bluetooth/common/ble_uart/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
# Both backends are listed; each .c file body is wrapped in
|
||||
# #if CONFIG_BT_NIMBLE_ENABLED / #if CONFIG_BT_BLUEDROID_ENABLED so only
|
||||
# the matching backend produces code. This is the standard IDF
|
||||
# pattern for conditional sources, because sdkconfig isn't loaded
|
||||
# during the early CMake component-requirement scan.
|
||||
idf_component_register(SRCS "ble_uart_nimble.c"
|
||||
"ble_uart_bluedroid.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES bt nvs_flash)
|
||||
@@ -1,11 +1,13 @@
|
||||
menu "BLE UART Example"
|
||||
menu "BLE UART library"
|
||||
|
||||
config BLE_UART_DEVICE_NAME_PREFIX
|
||||
string "BLE device name prefix"
|
||||
default "BleUart"
|
||||
help
|
||||
The firmware advertises as `<prefix>-XXXX` where XXXX is
|
||||
the last two bytes of the BT MAC in hex.
|
||||
Default prefix for examples that advertise as `<prefix>-XXXX`
|
||||
where XXXX is the last two bytes of the BT MAC in hex.
|
||||
Application code may ignore this and set an explicit name in
|
||||
ble_uart_config_t.
|
||||
|
||||
config BLE_UART_RX_SCRATCH_SIZE
|
||||
int "RX scratch buffer size (bytes)"
|
||||
@@ -1,7 +1,20 @@
|
||||
# BLE UART Porting & API Guide
|
||||
|
||||
This document lives in **`examples/bluetooth/common/ble_uart/`** next to the
|
||||
`ble_uart` component sources (`ble_uart.h`, backend `.c` files).
|
||||
|
||||
**Reference application:** use the **`examples/bluetooth/ble_uart_service`**
|
||||
example as the working template. Its root `CMakeLists.txt` appends this
|
||||
directory to **`EXTRA_COMPONENT_DIRS`** so `main` can `REQUIRES ble_uart`;
|
||||
`main/main.c` initializes NVS and a MAC-derived GAP name, calls
|
||||
`ble_uart_install()` / `ble_uart_open()` with the default encrypted UART-over-BLE echo
|
||||
path, and the tree ships `sdkconfig.defaults` plus the Bluedroid overlay
|
||||
(`sdkconfig.bluedroid`). Clone or diff that project when adapting to a new
|
||||
target or host stack.
|
||||
|
||||
A complete guide to integrating `ble_uart` into any ESP-IDF project.
|
||||
**Two or three files plus 5 steps of glue code** are enough to bring an
|
||||
**Either** `EXTRA_COMPONENT_DIRS` pointing at this component **or** a few
|
||||
copied source files **plus** the glue steps below are enough to bring an
|
||||
encrypted BLE serial peripheral up in a fresh project — the same
|
||||
`ble_uart.h` API works on top of either NimBLE or Bluedroid; pick the
|
||||
host with a Kconfig knob.
|
||||
@@ -18,7 +31,7 @@ flagged inline.
|
||||
|
||||
| Capability | Description |
|
||||
| --- | --- |
|
||||
| Standard Nordic UART Service GATT (RX/TX) | Interoperates with every generic BLE-serial tool (nRF Connect, Web Bluetooth, custom scripts) |
|
||||
| Widely used BLE UART-over-GATT (RX/TX) | Interoperates with every generic BLE-serial tool (mobile GATT clients, Web Bluetooth, custom scripts) |
|
||||
| LE Secure Connections + Bonding pairing | Single switch; when enabled, a fresh 6-digit passkey is printed to UART |
|
||||
| Auto-reconnect | After a bonded central disconnects, advertising restarts immediately and the LTK is reused — no passkey prompt |
|
||||
| Raw byte pass-through | RX is delivered via a callback; TX is exposed as `ble_uart_tx` |
|
||||
@@ -44,26 +57,45 @@ is entirely up to you**.
|
||||
|
||||
## 3. File Inventory
|
||||
|
||||
Files to copy into the target project — pick the backend you want and
|
||||
copy that pair plus the public header:
|
||||
Canonical sources live under **`$IDF_PATH/examples/bluetooth/common/ble_uart/`**
|
||||
(component name `ble_uart`): `ble_uart.h`, `ble_uart_nimble.c`,
|
||||
`ble_uart_bluedroid.c`, `CMakeLists.txt`, and `Kconfig` (prefix + RX scratch;
|
||||
`menuconfig → Component configuration → BLE UART library`). When reusing
|
||||
outside this tree, copy the whole `common/ble_uart/` directory or at least
|
||||
merge `Kconfig` into your component so the same `CONFIG_BLE_UART_*` symbols
|
||||
exist.
|
||||
|
||||
**Option A — depend on the in-tree component (no copy):** add the component
|
||||
directory to **`EXTRA_COMPONENT_DIRS` in the project root `CMakeLists.txt`
|
||||
before `include($ENV{IDF_PATH}/tools/cmake/project.cmake)` / `project()`**,
|
||||
then use `REQUIRES ble_uart` from `main/CMakeLists.txt` (see
|
||||
`examples/bluetooth/ble_uart_service/CMakeLists.txt`). This ensures the
|
||||
`ble_uart` target exists when CMake expands `main`'s requirements.
|
||||
|
||||
Kconfig options appear under
|
||||
`menuconfig → Component configuration → BLE UART library`.
|
||||
|
||||
> A `main/idf_component.yml` path dependency alone is **not** sufficient if
|
||||
> `main/CMakeLists.txt` lists `REQUIRES ble_uart`: the early requirement scan
|
||||
> runs before the component manager injects that dependency, so CMake fails
|
||||
> with *unknown component `ble_uart`*. Prefer `EXTRA_COMPONENT_DIRS` (as in the
|
||||
> reference example) or copy the sources into a normal project component.
|
||||
|
||||
**Option B — copy into your project:** pick the backend you want and
|
||||
copy that pair plus the public header (or copy both backends; each `.c`
|
||||
gates on its Kconfig symbol):
|
||||
|
||||
```
|
||||
your_project/main/
|
||||
├── ble_uart.h ← copy this (stack-agnostic public API, ~260 lines)
|
||||
├── ble_uart_nimble.c ← if you'll set CONFIG_BT_NIMBLE_ENABLED=y (~670 lines)
|
||||
└── ble_uart_bluedroid.c ← if you'll set CONFIG_BT_BLUEDROID_ENABLED=y (~900 lines)
|
||||
├── ble_uart.h ← copy from .../common/ble_uart/
|
||||
├── ble_uart_nimble.c ← if you'll set CONFIG_BT_NIMBLE_ENABLED=y
|
||||
└── ble_uart_bluedroid.c ← if you'll set CONFIG_BT_BLUEDROID_ENABLED=y
|
||||
```
|
||||
|
||||
You can also copy *both* `ble_uart_nimble.c` and `ble_uart_bluedroid.c`
|
||||
unchanged — each `.c` file gates its body on the matching Kconfig
|
||||
symbol, so the inactive one compiles to nothing. This is what the
|
||||
example itself does, and it lets you flip stacks without changing the
|
||||
source list.
|
||||
|
||||
Optional: `Kconfig.projbuild` defines `BLE_UART_DEVICE_NAME_PREFIX`
|
||||
and `BLE_UART_RX_SCRATCH_SIZE`. Copy it too if you want either to be
|
||||
tunable from `menuconfig`; otherwise hard-code the name in your
|
||||
source and rely on the 1024-byte fallback for RX scratch.
|
||||
Optional: copy `Kconfig` from `common/ble_uart/` into your component (or merge
|
||||
its symbols into your own `Kconfig`) if you want `BLE_UART_*` in `menuconfig`;
|
||||
otherwise hard-code the device name and rely on the 1024-byte fallback for RX
|
||||
scratch.
|
||||
|
||||
---
|
||||
|
||||
@@ -75,11 +107,12 @@ Assume you already have an ESP-IDF project (`my_project/`).
|
||||
|
||||
```bash
|
||||
cd my_project/main
|
||||
BLE_UART_SRC="$IDF_PATH/examples/bluetooth/common/ble_uart"
|
||||
# Stack-agnostic public header — always.
|
||||
cp /path/to/ble_uart_service/main/ble_uart.h .
|
||||
cp "$BLE_UART_SRC/ble_uart.h" .
|
||||
# Pick one (or copy both — the inactive one compiles to nothing).
|
||||
cp /path/to/ble_uart_service/main/ble_uart_nimble.c .
|
||||
cp /path/to/ble_uart_service/main/ble_uart_bluedroid.c .
|
||||
cp "$BLE_UART_SRC/ble_uart_nimble.c" .
|
||||
cp "$BLE_UART_SRC/ble_uart_bluedroid.c" .
|
||||
```
|
||||
|
||||
### 4.2 Edit `main/CMakeLists.txt`
|
||||
@@ -116,6 +149,22 @@ the central must support it.
|
||||
|
||||
**Bluedroid backend (drop-in alternative):**
|
||||
|
||||
Use the **`examples/bluetooth/ble_uart_service/sdkconfig.bluedroid`** file as
|
||||
the authoritative Kconfig overlay: it enables the host stack, SMP, GATTS
|
||||
(service-table API), and the BLE-only advertising knobs that
|
||||
`ble_uart_bluedroid.c` expects. Either merge those lines into your own
|
||||
`sdkconfig.defaults`, or pass them as a second defaults file:
|
||||
|
||||
```bash
|
||||
idf.py -D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.bluedroid" reconfigure
|
||||
```
|
||||
|
||||
(Paths are relative to the example project root; copy `sdkconfig.bluedroid`
|
||||
into your tree if you are not starting from `ble_uart_service`.)
|
||||
|
||||
A minimal inline sketch (may drift from IDF defaults — **diff against
|
||||
`sdkconfig.bluedroid` after each IDF upgrade**):
|
||||
|
||||
```ini
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_ENABLED=n
|
||||
@@ -124,8 +173,8 @@ CONFIG_BT_BLUEDROID_ENABLED=y
|
||||
# LE Secure Connections + bonding (Bluedroid persists LTKs by default)
|
||||
CONFIG_BT_BLE_SMP_ENABLE=y
|
||||
|
||||
# Optional: bigger MTU
|
||||
CONFIG_BT_GATT_MAX_MTU_SIZE=512
|
||||
# Optional: bigger MTU (when supported by your IDF target / menuconfig)
|
||||
# CONFIG_BT_GATT_MAX_MTU_SIZE=512
|
||||
|
||||
# BLE-only feature set (saves flash on classic-BT-capable parts)
|
||||
CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y
|
||||
@@ -201,7 +250,7 @@ I (xxx) ble_uart: registered service svc_handle=40 rx=42 tx=44 cccd=45
|
||||
I (xxx) ble_uart: advertising started
|
||||
```
|
||||
|
||||
nRF Connect on a phone discovers `MyDevice`; connect, enter the
|
||||
A phone GATT client app discovers `MyDevice`; connect, enter the
|
||||
passkey, subscribe to TX, write to RX, and you will see the echo come
|
||||
back.
|
||||
|
||||
@@ -337,7 +386,7 @@ returns `ENOTCONN` to tell you.
|
||||
extern const ble_uart_uuid128_t ble_uart_service_uuid;
|
||||
```
|
||||
|
||||
Always `6e400001-b5a3-f393-e0a9-e50e24dcca9e` (the NUS standard). It is
|
||||
Always `6e400001-b5a3-f393-e0a9-e50e24dcca9e` (the de-facto BLE UART service UUID). It is
|
||||
already inserted into the scan response, so the **application normally
|
||||
does not touch it**. You only need it if you take over advertising
|
||||
yourself (see 6.3).
|
||||
@@ -403,7 +452,7 @@ Effect:
|
||||
- GATT characteristics drop the `_ENC | _AUTHEN` flags.
|
||||
- Any central can read/write — no pairing required.
|
||||
- No passkey prompt.
|
||||
- Data is sniffable by any nearby nRF dongle.
|
||||
- Data is sniffable by any nearby BLE sniffer or compromised radio in range.
|
||||
|
||||
**Do not ship this in production firmware.**
|
||||
|
||||
@@ -439,7 +488,10 @@ ble_uart_open();
|
||||
|
||||
### 6.4 Configuring the device-name prefix via Kconfig
|
||||
|
||||
Copy `Kconfig.projbuild` into `main/`, then:
|
||||
If you use the shared `ble_uart` component, options are already in
|
||||
`menuconfig → Component configuration → BLE UART library`. If you copied only
|
||||
the `.c` / `.h` files into `main/`, copy `Kconfig` from `common/ble_uart/` as
|
||||
well (or merge its symbols into your own `Kconfig.projbuild`), then:
|
||||
|
||||
```c
|
||||
char name[24];
|
||||
@@ -453,8 +505,8 @@ ble_uart_install(&(ble_uart_config_t){
|
||||
});
|
||||
```
|
||||
|
||||
Edit the default through `menuconfig → BLE UART Example → BLE device
|
||||
name prefix`.
|
||||
Edit the default through `menuconfig → Component configuration → BLE UART
|
||||
library → BLE device name prefix`.
|
||||
|
||||
### 6.5 Pushing data proactively
|
||||
|
||||
@@ -538,16 +590,17 @@ If you **build directly on top of this example**:
|
||||
| --- | --- |
|
||||
| `main.c` echo template | Replace with your own `on_rx` body |
|
||||
| `sdkconfig.defaults` | Reuse as-is |
|
||||
| `Kconfig.projbuild` | Reuse as-is |
|
||||
| `sdkconfig.bluedroid` | Only if you switch to Bluedroid host — reuse as-is (see §4.3); omit for default NimBLE |
|
||||
| Root `CMakeLists.txt` (`EXTRA_COMPONENT_DIRS` → `../common/ble_uart`) | Reuse as-is (or follow §3 option B) |
|
||||
| `CMakeLists.txt` (root + main) | Reuse as-is |
|
||||
|
||||
If you **start from an empty project**:
|
||||
|
||||
| What you need to do | Source |
|
||||
| --- | --- |
|
||||
| Copy `ble_uart.h` + at least one of `ble_uart_nimble.c` / `ble_uart_bluedroid.c` into `main/` | This example |
|
||||
| Add `EXTRA_COMPONENT_DIRS` for `examples/bluetooth/common/ble_uart` in root `CMakeLists.txt`, **or** copy `ble_uart.h` + at least one of `ble_uart_nimble.c` / `ble_uart_bluedroid.c` into `main/` | §3 of this guide |
|
||||
| Copy the key lines of `sdkconfig.defaults` | §4.3 of this guide |
|
||||
| Add SRC + REQUIRES to `main/CMakeLists.txt` | §4.2 of this guide |
|
||||
| Add `REQUIRES ble_uart nvs_flash` (after `EXTRA_COMPONENT_DIRS`) **or** SRC + `REQUIRES bt nvs_flash` (copied sources) to `main/CMakeLists.txt` | §4.2 of this guide |
|
||||
| Write `install` + `open` in `app_main` | §4.4 of this guide |
|
||||
|
||||
---
|
||||
@@ -591,8 +644,21 @@ extern const ble_uart_uuid128_t ble_uart_service_uuid;
|
||||
|
||||
## 12. Minimal Project Template (ready to flash)
|
||||
|
||||
A complete flashable project takes 7 files (the inactive backend `.c`
|
||||
compiles to nothing, so it costs you nothing to ship both):
|
||||
A complete flashable project takes a handful of files. The inactive
|
||||
backend `.c` compiles to nothing if you ship both.
|
||||
|
||||
**Using the shared component (fewer copies):**
|
||||
|
||||
```
|
||||
my_ble_uart_project/
|
||||
├── CMakeLists.txt ← EXTRA_COMPONENT_DIRS → …/common/ble_uart (before project())
|
||||
├── sdkconfig.defaults
|
||||
└── main/
|
||||
├── CMakeLists.txt ← REQUIRES ble_uart nvs_flash; SRCS main.c only
|
||||
└── main.c
|
||||
```
|
||||
|
||||
**Copying sources into `main/` (classic layout):**
|
||||
|
||||
```
|
||||
my_ble_uart_project/
|
||||
@@ -600,20 +666,28 @@ my_ble_uart_project/
|
||||
├── sdkconfig.defaults
|
||||
└── main/
|
||||
├── CMakeLists.txt
|
||||
├── ble_uart.h ← copied from this example
|
||||
├── ble_uart_nimble.c ← copied from this example
|
||||
├── ble_uart_bluedroid.c ← copied from this example (optional)
|
||||
├── ble_uart.h ← from $IDF_PATH/examples/bluetooth/common/ble_uart/
|
||||
├── ble_uart_nimble.c
|
||||
├── ble_uart_bluedroid.c ← optional second backend
|
||||
└── main.c
|
||||
```
|
||||
|
||||
**Root `CMakeLists.txt`**:
|
||||
**Root `CMakeLists.txt`** (shared `ble_uart` via `EXTRA_COMPONENT_DIRS`):
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "${CMAKE_CURRENT_LIST_DIR}/../path/to/common/ble_uart")
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(my_ble_uart)
|
||||
```
|
||||
|
||||
**`main/CMakeLists.txt`**:
|
||||
**`main/CMakeLists.txt`** (shared component — no `.c` copies in `main/`):
|
||||
```cmake
|
||||
idf_component_register(SRCS "main.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES ble_uart nvs_flash)
|
||||
```
|
||||
|
||||
**`main/CMakeLists.txt`** (classic copy layout — both backends in `main/`):
|
||||
```cmake
|
||||
idf_component_register(SRCS "main.c"
|
||||
"ble_uart_nimble.c"
|
||||
@@ -633,6 +707,11 @@ CONFIG_BT_NIMBLE_NVS_PERSIST=y
|
||||
CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU=512
|
||||
```
|
||||
|
||||
**Bluedroid host instead of NimBLE:** copy
|
||||
`examples/bluetooth/ble_uart_service/sdkconfig.bluedroid` next to your
|
||||
`sdkconfig.defaults` and pass
|
||||
`-D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.bluedroid"` (see §4.3).
|
||||
|
||||
**`main/main.c`** — copy the §4.4 template verbatim.
|
||||
|
||||
Flash:
|
||||
@@ -5,8 +5,8 @@
|
||||
*
|
||||
* BLE UART — turnkey serial-over-BLE peripheral.
|
||||
*
|
||||
* Implements the de-facto Nordic UART Service (NUS) GATT layout
|
||||
* (RX write, TX notify) on top of either NimBLE or Bluedroid; the
|
||||
* Implements the de-facto BLE UART-over-GATT layout (RX write, TX notify;
|
||||
* fixed 128-bit UUIDs below) on top of either NimBLE or Bluedroid; the
|
||||
* backend is picked at compile time via CONFIG_BT_NIMBLE_ENABLED /
|
||||
* CONFIG_BT_BLUEDROID_ENABLED.
|
||||
*
|
||||
@@ -21,13 +21,13 @@
|
||||
* Run-forever apps only need install + open. close / uninstall is
|
||||
* for apps that need to power BLE off at runtime.
|
||||
*
|
||||
* GATT layout (UUIDs fixed by the NUS spec):
|
||||
* GATT layout (UUIDs are the widely used fixed 128-bit values):
|
||||
*
|
||||
* Service: 6e400001-b5a3-f393-e0a9-e50e24dcca9e
|
||||
* RX : 6e400002-b5a3-f393-e0a9-e50e24dcca9e write
|
||||
* TX : 6e400003-b5a3-f393-e0a9-e50e24dcca9e notify
|
||||
*
|
||||
* See PORTING.md for the integration guide.
|
||||
* See PORTING.md in this component directory for the integration guide.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -89,7 +89,7 @@ typedef struct {
|
||||
|
||||
/* ----- Lifecycle ------------------------------------------------------ */
|
||||
|
||||
/** Bring up host stack + Security Manager + SIG services + NUS GATT
|
||||
/** Bring up host stack + Security Manager + SIG services + BLE UART GATT
|
||||
* service. Caller must have already called nvs_flash_init().
|
||||
* cfg->device_name is copied; doesn't need to outlive the call.
|
||||
* Single-shot until ble_uart_uninstall(); a second call returns
|
||||
@@ -145,7 +145,7 @@ bool ble_uart_is_subscribed(void);
|
||||
|
||||
/* ----- Service UUID -------------------------------------------------- */
|
||||
|
||||
/** The NUS service UUID, exposed for custom advertising payloads.
|
||||
/** The BLE UART service UUID, exposed for custom advertising payloads.
|
||||
* The two characteristic UUIDs are private to the backend. */
|
||||
extern const ble_uart_uuid128_t ble_uart_service_uuid;
|
||||
|
||||
@@ -204,7 +204,7 @@ static void build_attr_table(bool encrypted)
|
||||
},
|
||||
};
|
||||
|
||||
/* [TX value] — NUS spec is notify-only, so the prop above doesn't
|
||||
/* [TX value] — TX characteristic is notify-only, so the prop above doesn't
|
||||
* advertise READ; perm only matters if a client tries READ anyway. */
|
||||
s_nus_db[NUS_IDX_TX_VAL] = (esp_gatts_attr_db_t){
|
||||
.attr_control = {ESP_GATT_AUTO_RSP},
|
||||
@@ -251,7 +251,7 @@ static esp_ble_adv_data_t s_adv_data = {
|
||||
.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
|
||||
};
|
||||
|
||||
/* Scan response = NUS UUID. Splitting it off the primary payload
|
||||
/* Scan response = 128-bit UART service UUID. Splitting it off the primary payload
|
||||
* leaves room for name + tx_pwr in the 31-byte primary. */
|
||||
static esp_ble_adv_data_t s_scan_rsp_data = {
|
||||
.set_scan_rsp = true,
|
||||
@@ -64,7 +64,7 @@ extern void ble_store_config_init(void);
|
||||
|
||||
/* ===== UUIDs =========================================================== */
|
||||
|
||||
/* NUS UUIDs in little-endian byte order. */
|
||||
/* BLE UART profile UUIDs in little-endian byte order. */
|
||||
#define NUS_SVC_BYTES 0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0, \
|
||||
0x93, 0xf3, 0xa3, 0xb5, 0x01, 0x00, 0x40, 0x6e
|
||||
#define NUS_RX_BYTES 0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0, \
|
||||
@@ -80,8 +80,9 @@ static const ble_uuid128_t s_chr_tx_uuid = BLE_UUID128_INIT(NUS_TX_BYTES);
|
||||
|
||||
/* ===== State =========================================================== */
|
||||
|
||||
/* RX scratch capacity. Tunable via menuconfig; fall back to 1024 if
|
||||
* Kconfig.projbuild isn't carried along when reusing this file. */
|
||||
/* RX scratch capacity. Tunable via menuconfig (Component config → BLE UART
|
||||
* library); fall
|
||||
* back to 1024 if CONFIG_BLE_UART_RX_SCRATCH_SIZE is absent. */
|
||||
#ifndef CONFIG_BLE_UART_RX_SCRATCH_SIZE
|
||||
#define CONFIG_BLE_UART_RX_SCRATCH_SIZE 1024
|
||||
#endif
|
||||
@@ -106,7 +107,7 @@ static uint8_t s_own_addr_type;
|
||||
static int gap_event(struct ble_gap_event *event, void *arg);
|
||||
static int start_advertising(void);
|
||||
|
||||
/* ===== GATT (NUS) ====================================================== */
|
||||
/* ===== GATT (BLE UART service) ========================================= */
|
||||
|
||||
static int chr_access(uint16_t conn_handle, uint16_t attr_handle,
|
||||
struct ble_gatt_access_ctxt *ctxt, void *arg)
|
||||
@@ -253,7 +254,7 @@ static int start_advertising(void)
|
||||
{
|
||||
/* 31-byte primary adv can't hold flags + tx_pwr + name + 128-bit
|
||||
* UUID together, so split: primary = flags+tx_pwr+name,
|
||||
* scan rsp = NUS UUID. */
|
||||
* scan rsp = 128-bit service UUID. */
|
||||
const char *name = s_dev_name;
|
||||
size_t name_len = strlen(name);
|
||||
|
||||
@@ -262,7 +263,7 @@ static int start_advertising(void)
|
||||
.tx_pwr_lvl_is_present = 1,
|
||||
.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO,
|
||||
/* If no name was set, advertise without one (NimBLE accepts
|
||||
* NULL+0); the NUS UUID in scan rsp still identifies us. */
|
||||
* NULL+0); the service UUID in scan rsp still identifies us. */
|
||||
.name = name_len > 0 ? (uint8_t *)name : NULL,
|
||||
.name_len = name_len,
|
||||
.name_is_complete = name_len > 0 ? 1 : 0,
|
||||
@@ -62,7 +62,7 @@ Open an interactive BLE UART Console:
|
||||
python main.py console DEVICE_ID
|
||||
```
|
||||
|
||||
For Console options such as line endings, hex mode, and write-with-response, see [Quick-Start-BLE-UART-Console.md](docs/Quick-Start-BLE-UART-Console.md). If you need firmware to test against, use the [BLE UART Service example](../../../examples/bluetooth/ble_uart_service) as an Echo Server: it advertises the default Nordic UART Service profile and echoes RX writes back through TX notifications.
|
||||
For Console options such as line endings, hex mode, and write-with-response, see [Quick-Start-BLE-UART-Console.md](docs/Quick-Start-BLE-UART-Console.md). If you need firmware to test against, use the [BLE UART Service example](../../../examples/bluetooth/ble_uart_service) as an Echo Server: it advertises the default BLE UART-over-GATT UUIDs and echoes RX writes back through TX notifications.
|
||||
|
||||
Run the BLE UART Daemon:
|
||||
|
||||
@@ -194,7 +194,7 @@ Main responsibilities:
|
||||
- Connect and disconnect with a BLE UART GATT profile.
|
||||
- Subscribe to device-to-host notifications.
|
||||
- Send host-to-device data as `str`, `bytes`, or `bytearray`.
|
||||
- Support a default NUS profile and user-defined BLE UART profiles.
|
||||
- Support a default BLE-UART UUID profile and user-defined BLE UART profiles.
|
||||
|
||||
Important APIs:
|
||||
|
||||
@@ -262,7 +262,7 @@ Use Core when your business logic lives in Python. Use Console when you only nee
|
||||
|
||||
## Profile compatibility
|
||||
|
||||
The default profile is compatible with the Nordic UART Service (NUS):
|
||||
The default profile uses the widely deployed BLE UART-over-GATT UUID set:
|
||||
|
||||
- Service UUID: `6E400001-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||
- RX characteristic UUID, host to device: `6E400002-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||
|
||||
@@ -42,7 +42,7 @@ flowchart LR
|
||||
OC -->|permission.asked| Plugin
|
||||
Plugin -->|POST /notify| Daemon[ble_uart_bridge daemon]
|
||||
Plugin -->|POST /request| Daemon
|
||||
Daemon -->|BLE NUS JSONL| Device[BLE device UI]
|
||||
Daemon -->|BLE UART JSONL| Device[BLE device UI]
|
||||
Device -->|once / reject| Daemon
|
||||
Daemon -->|HTTP response| Plugin
|
||||
Plugin -->|SDK permission reply| OC
|
||||
@@ -54,7 +54,7 @@ flowchart LR
|
||||
|
||||
The intended firmware companion is an `esp-vocat` example for the MiaoBan
|
||||
(喵伴) device, planned for the `esp-iot-solution` repository. Until that
|
||||
example is available, use any device that implements Nordic UART Service and
|
||||
example is available, use any device that implements the default BLE UART-over-GATT UUIDs and
|
||||
the JSONL request/response envelope described in
|
||||
[Firmware protocol reference](#firmware-protocol-reference).
|
||||
|
||||
@@ -235,7 +235,7 @@ permission requests can be approved once with `once` or denied with `reject`.
|
||||
- The BLE daemon endpoint is configured by `OPENCODE_BLE_DAEMON_URL`, defaulting
|
||||
to `http://127.0.0.1:8888`.
|
||||
- The BLE daemon supports both `POST /notify` and `POST /request`.
|
||||
- The BLE device implements Nordic UART Service.
|
||||
- The BLE device implements the default BLE UART-over-GATT UUID layout.
|
||||
- The BLE device understands JSON messages described in
|
||||
[Firmware protocol reference](#firmware-protocol-reference).
|
||||
- Permission decisions from the current single-key device are: `once`, `reject`.
|
||||
|
||||
@@ -141,7 +141,7 @@ await bridge.send(b"\x01\x02", with_response=True)
|
||||
|
||||
## Use a custom BLE UART profile
|
||||
|
||||
The default profile uses Nordic UART Service UUIDs. For custom firmware, create a `BLEUARTProfile`:
|
||||
The default profile uses the de-facto BLE UART-over-GATT UUIDs. For custom firmware, create a `BLEUARTProfile`:
|
||||
|
||||
```python
|
||||
from src.core import BLEUARTBridge
|
||||
|
||||
@@ -8,9 +8,9 @@ BLE UART Bridge works with BLE GATT profiles that provide a UART-like data path:
|
||||
- one characteristic that the host writes to
|
||||
- one characteristic that the device uses to notify data back to the host
|
||||
|
||||
The default profile is compatible with the Nordic UART Service (NUS), but NUS is not the only possible BLE UART-style profile.
|
||||
The default profile matches the widely used BLE UART-over-GATT UUID set (service `6E400001-…`, RX/TX characteristics), but that layout is not the only possible BLE UART-style profile.
|
||||
|
||||
## Default NUS-compatible profile
|
||||
## Default BLE-UART-compatible profile
|
||||
|
||||
The built-in default profile uses these UUIDs:
|
||||
|
||||
@@ -20,7 +20,7 @@ The built-in default profile uses these UUIDs:
|
||||
| RX, host to device | `6E400002-B5A3-F393-E0A9-E50E24DCCA9E` |
|
||||
| TX, device to host | `6E400003-B5A3-F393-E0A9-E50E24DCCA9E` |
|
||||
|
||||
Use the default profile when the device advertises a NUS-compatible service.
|
||||
Use the default profile when the device advertises a service using those UUIDs.
|
||||
|
||||
## ESP-IDF BLE SPP examples
|
||||
|
||||
@@ -31,7 +31,7 @@ ESP-IDF includes BLE SPP examples that implement Espressif BLE UART-like vendor-
|
||||
- `examples/bluetooth/bluedroid/ble/ble_spp_server`
|
||||
- `examples/bluetooth/bluedroid/ble/ble_spp_client`
|
||||
|
||||
BLE SPP over BLE is not a Bluetooth SIG standard profile. It is a vendor-specific GATT design that emulates a serial link, similar in purpose to NUS.
|
||||
BLE SPP over BLE is not a Bluetooth SIG standard profile. It is a vendor-specific GATT design that emulates a serial link, similar in purpose to the default BLE UART layout above.
|
||||
|
||||
ESP-IDF BLE SPP examples may define more characteristics than BLE UART Bridge needs, such as data, command, and status characteristics. To use BLE UART Bridge with such a profile, map only the UART-like data path into `BLEUARTProfile`.
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ The Console is useful when you want to type data into a BLE UART device and insp
|
||||
|
||||
On Windows, run `export.bat` or `export.ps1` from the ESP-IDF root directory before installing `requirements.txt`. If you use your own Python virtual environment instead, activate it before installing `requirements.txt`.
|
||||
|
||||
3. A BLE device advertising the BLE UART service. By default the tool scans for Nordic UART Service UUIDs. For a known-compatible test target, build and flash the [BLE UART Service example](../../../../examples/bluetooth/ble_uart_service), which acts as an Echo Server by echoing RX writes back through TX notifications.
|
||||
3. A BLE device advertising the BLE UART service. By default the tool scans for the de-facto BLE UART-over-GATT UUIDs (`6E400001-…` / `…02` / `…03`). For a known-compatible test target, build and flash the [BLE UART Service example](../../../../examples/bluetooth/ble_uart_service), which acts as an Echo Server by echoing RX writes back through TX notifications.
|
||||
|
||||
## Find a device
|
||||
|
||||
|
||||
Reference in New Issue
Block a user