RT-Thread Device Framework Bluetooth (BT) Example
Source code path: example/rt_device/bt
This example demonstrates how to drive the SiFli Bluetooth subsystem entirely through the RT-Thread standard device framework (rt_device_find / rt_device_control + event callbacks), instead of calling the underlying bt_interface_* protocol stack APIs directly.
SiFli SDK registers Bluetooth as an RT-Thread miscellaneous device named bt_device. This device is special in that it only implements the control() operation. Opening, closing, registering event callbacks, querying status, and performing actions for each profile are all done through rt_device_control with different BT_CONTROL_* command codes. Asynchronous results are reported through a registered bt_notify_cb callback as BT_EVENT_* events.
On this basis, the Bluetooth application is organized as a multi-service “core + plugins” framework: the core has been extracted into an independent middleware component, rt_bt_app_core (located in middleware/rt_bt_app_core/), which can be reused by any project. Adding a new Bluetooth profile (HFP, SPP, etc.) only requires adding one .c file and one configuration option on the application side, without modifying the core or other services.
Supported Platforms
sf32lb52 series
sf32lb56 series
sf32lb58 series
1. Directory Structure
The core framework is an independent middleware component. The example project keeps only the entry point and the service plugins for each profile:
middleware/rt_bt_app_core/ [core framework, independent reusable middleware component]
├── rt_bt_app.h framework contract: service descriptor / command table / registration macros / interfaces
├── rt_bt_app_core.c device management, event thread and queue, built-in common event handling, service registry
├── rt_bt_app_cmd.c command router for all services
├── Kconfig BT_USING_RT_BT_APP_CORE and its sub-options
└── SConscript
example/rt_device/bt/
├── project/ project directory
├── src/
│ ├── main.c entry point: initialize the core + enable the protocol stack
│ └── services/
│ ├── SConscript selects which service files to compile based on BT_USING_XXX macros
│ ├── bt_srv_hfp.c HFP service (complete implementation)
│ └── bt_srv_spp.c SPP service skeleton (teaching template, demonstrates a simple framework, not full SPP functionality)
└── README.md
Core: middleware component
rt_bt_app_core(rt_bt_app_core.c/rt_bt_app_cmd.c). It holds thebt_devicehandle, runs the event service thread, deep-copies events before routing them to the appropriate service by the high byte of the event code, handles common events internally (stack ready / inquiry / connection / disconnection), and provides the top-levelbtshell command. The core does not need to change when adding a new profile.Service:
services/bt_srv_xxx.c. Each profile has one file, and each file is a self-registering service that provides three things: a command table, an event handler, and an optional event deep-copy hook.
2. How the Framework Works
1. How events are routed to the correct service
The high byte of each profile’s event code is fixed (see bt_device.h):
High byte |
Macro |
Event group |
|---|---|---|
0x40 |
|
Common events (stack ready / inquiry / connection / disconnection), handled internally by the core |
0x41 |
|
HFP |
0x46 |
|
SPP |
… |
… |
… |
After the core receives an event, it uses event >> 8 to get the high byte, finds the service whose event_group matches it, and calls that service’s on_event. If no service claims it and the high byte is BT_COMMON_TYPE_ID, the core falls back to its built-in common event handling.
2. How commands are routed to the correct service
The top-level command is bt, with three levels of routing:
bt list all registered services
bt <service> list the subcommands of one service
bt <service> <cmd> [args] execute a subcommand
rt_bt_app_cmd.c is a generic router and does not know any specific profile. It first finds the service by name, then finds the subcommand by name in that service’s command table, checks whether the protocol stack needs to be ready, and finally calls the handler registered in the command table.
3. How to Add a New Bluetooth Service
Taking a new xxx service as an example, there are three steps:
Create
bt_srv_xxx.cundersrc/services/, implement the following parts, and register it automatically:Command wrapper: convert actions into
rt_bt_app_control(BT_CONTROL_XXX, &arg);Command table
rt_bt_cmd_entry_t[]: map shell subcommands to the wrappers above;Event handler
xxx_on_event: handle the profile’sBT_EVENT_*events;Deep-copy hook
xxx_clone(optional, required when event parameters contain pointers);End with
RT_BT_SERVICE_REGISTER(xxx_service);for self-registration.
Add one line to
src/services/SConscriptto gate compilation with a driver macro:if GetDepend('BT_USING_XXX'): src += ['bt_srv_xxx.c']
Enable the corresponding
CONFIG_BT_USING_XXX=yin menuconfig /proj.conf.
You can copy bt_srv_spp.c as a starting skeleton.
The service depends only on the framework contract provided by
rt_bt_app.h. Including it gives you access to interfaces such asrt_bt_service_t,rt_bt_cmd_entry_t,rt_bt_app_control(), andRT_BT_SERVICE_REGISTER().
4. Configuration and Dependencies
The core framework is controlled by the Kconfig option BT_USING_RT_BT_APP_CORE and depends on BSP_BLE_SIBLES and BT_FINSH. This example’s proj.conf already enables:
CONFIG_BT_USING_RT_BT_APP_CORE=y # enable the core framework component
CONFIG_BT_APP_ENABLE_SHELL_CMD=y # enable the "bt" shell command (optional)
5. Build and Flash
In the project directory (example/rt_device/bt/project):
scons --board=sf32lb56-lcd_a128r12n1 -j8
The build artifacts are generated under the build_<board_name> directory. Enter that directory and run uart_download.bat to flash the firmware as prompted:
build_sf32lb56-lcd_a128r12n1_hcpu\uart_download.bat
6. Expected Results
1. Boot log
After reset, you should see the following key messages:
BT device "bt_device" found # rt_device_find succeeded
service "hfp" registered (group 0x41) # HFP service self-registered
...
BT stack ready # protocol stack is ready (the core then automatically powers on and sets the local name)
2. Command overview
bt # list all services
bt hfp # list all HFP subcommands
3. HFP subcommands
Command |
Description |
|---|---|
|
Delete all paired devices (uses the connection management interface, not a device framework command) |
|
Search for nearby Bluetooth devices (no device-class restriction; search all devices) |
|
Stop searching |
|
Establish an HFP connection with a phone (address format |
|
Disconnect the HFP connection |
|
Query the local phone number (subscriber number) |
|
Query the remote current call list (CLCC) |
|
Query the remote call status |
|
Place a call |
|
Redial the last number |
|
Answer an incoming call |
|
Hang up the current call |
|
Set the call speaker volume |
|
Establish the call audio (SCO) link |
|
Disconnect the call audio (SCO) link |
|
Report the local battery level through HFP |
4. Typical verification flow (HFP)
Since this device will be discoverable, the most direct approach is to have a phone connect proactively, or connect directly using a known address:
bt hfp start_inquiry # search; the log prints the device address
bt hfp hfp_connect 22:5a:46:6d:42:e0 # connect using the address from the log
# on success, prints "profile 0x0 connected"
bt hfp make_call 10086 # dial
bt hfp answer_call # can be controlled locally after the other side answers
bt hfp handup_call # hang up