trouble
trouble copied to clipboard
`BleHost(Disconnected)` when setting connection param
I'm testing nrf-sdc updating connection parameter after GattConnection is established. I use
conn.raw().update_connection_params(&stack,
&ConnectParams {
min_connection_interval: Duration::from_micros(7500),
max_connection_interval: Duration::from_micros(7500),
max_latency: 99,
event_length: Duration::from_secs(0),
supervision_timeout: Duration::from_secs(5),
})`
to update connections parameters. But this function always returns BleHost(Disconnected). I'm not sure that whether the connection parameters are updated, but according to
https://github.com/embassy-rs/trouble/blob/d17132618413f0fa840fee32af4ab23ca125bec2/host/src/connection.rs#L415-L417
it's because that the conn handler is not recognized by the controller, which seems wrong. The data exchange of the connection runs well before and after calling update_connection_params
Is this on the central side or on the peripheral side you're calling this? I'm using this successfully, so I'm surprised that you get this error. Could you share more on how you create the GattConnection?
On the peripheral side.
I create the connection use:
async fn advertise<'a, 'b, C: Controller>(
name: &'a str,
peripheral: &mut Peripheral<'a, C>,
server: &'b Server<'_>,
) -> Result<GattConnection<'a, 'b>, BleHostError<C::Error>> {
// Wait for 10ms to ensure the USB is checked
embassy_time::Timer::after_millis(10).await;
let mut advertiser_data = [0; 31];
AdStructure::encode_slice(
&[
AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
AdStructure::ServiceUuids16(&[BATTERY.to_le_bytes(), HUMAN_INTERFACE_DEVICE.to_le_bytes()]),
AdStructure::CompleteLocalName(name.as_bytes()),
AdStructure::Unknown {
ty: 0x19, // Appearance
data: &KEYBOARD.to_le_bytes(),
},
],
&mut advertiser_data[..],
)?;
let advertise_config = AdvertisementParameters {
primary_phy: PhyKind::Le2M,
secondary_phy: PhyKind::Le2M,
tx_power: TxPower::Plus8dBm,
timeout: Some(Duration::from_secs(120)),
interval_min: Duration::from_millis(200),
interval_max: Duration::from_millis(200),
..Default::default()
};
info!("[adv] advertising");
let advertiser = peripheral
.advertise(
&advertise_config,
Advertisement::ConnectableScannableUndirected {
adv_data: &advertiser_data[..],
scan_data: &[],
},
)
.await?;
let conn = advertiser.accept().await?.with_attribute_server(server)?;
info!("[adv] connection established");
Ok(conn)
}
The attribute server is a little bit complex, can be found here:
https://github.com/HaoboGu/rmk/blob/98af3181d2d06ecd6335840319e4a585a1ec25eb/rmk/src/ble/trouble/ble_server.rs#L10
I also got some attribute not found errors, when connecting to linux as a peripheral, the following are HCI log on the linux(central):
The linux machine reports malformed att response
Forgot to ask, which controller, nrf-sdc?
Forgot to ask, which controller, nrf-sdc?
yes, nrf-sdc
After more investigation, I found this config only fails on Linux and Android. It works on macOS, iOS and Windows.
Hi, you might want to check the nrf softdevice controller docs for this command: https://github.com/nrfconnect/sdk-nrfxlib/blob/main/softdevice_controller/include/sdc_hci_cmd_le.h#L3471-L3477
If the Host issues this command on a Connection_Handle where the Controller is the
Peripheral and either the local or peer Controller does not support the Connection
Parameters Request procedure (see [Vol 6] Part B, Section 5.1.7), then the Controller
shall return an error which should use the error code Unsupported Feature or
Parameter Value (0x11) if the local Controller does not support that procedure and
Unsupported Remote Feature (0x1A) if the local Controller supports that procedure but
the peer Controller does not.
So could be that there are some bluetooth version differences.
It's quite weird, sometimes I get UnknownHCICommand as well:
ERROR [set_conn_params] error: "BleHost(Hci(Unknown HCI Command))"
└─ rmk::ble::trouble::set_conn_params::{async_fn#0} @ /Users/haobogu/Projects/keyboard/rmk/rmk/src/ble/trouble/mod.rs:483
It seems that different centrals(windows/linux/..) will result in different error message. I totally have no idea about what happened..
Just did some more testing. On macOS/iOS, it returns BleHost(Disconnected), and on Linux/Android it returns BleHost(UnknownHCICommand).
@lulf Now I can reproduce it on my trouble fork, see this commit:
https://github.com/haobogu/trouble/commit/887563959f975a64c46cb2083d3bb2bffa0f40c4
The only difference is setting conn param after the connection is established and several logs.
I got the following log:
0.002258 [INFO ] Our address = FF:E4:05:1A:8F:FF (trouble_example_apps apps/src/fmt.rs:143)
0.002624 [INFO ] Starting advertising and GATT service (trouble_example_apps apps/src/fmt.rs:143)
0.002838 [INFO ] [host] using packet pool with MTU 251 capacity 16 (trouble_host host/src/fmt.rs:143)
0.002899 [INFO ] [host] filter accept list size: 8 (trouble_host host/src/fmt.rs:143)
0.002929 [INFO ] [host] setting txq to 3, fragmenting at 72 (trouble_host host/src/fmt.rs:143)
0.002990 [INFO ] [host] configuring host buffers (1 packets of size 255) (trouble_host host/src/fmt.rs:143)
0.003051 [INFO ] [host] initialized (trouble_host host/src/fmt.rs:143)
0.003173 [TRACE] [host] enabling advertising (trouble_host host/src/fmt.rs:117)
0.003326 [INFO ] [adv] advertising (trouble_example_apps apps/src/fmt.rs:143)
4.788879 [TRACE] [host] connection with handle ConnHandle(0) established to BdAddr([d2, 49, 6d, d4, b7, 6b]) (trouble_host host/src/fmt.rs:117)
4.789031 [TRACE] [link][poll_accept] connection accepted: state: state = Connected, conn = Some(ConnHandle(0)), flow = 3, role = Some(Peripheral), peer = Some(BdAddr(BdAddr([D2, 49, 6D, D4, B7, 6B])) Irk(None)), ref = 0, sar = PacketReassembly { state: None } (trouble_host host/src/fmt.rs:117)
4.789276 [TRACE] [gatt 0] connecting to server (trouble_host host/src/fmt.rs:117)
4.789306 [TRACE] [server] searching for peer BdAddr(BdAddr([D2, 49, 6D, D4, B7, 6B])) Irk(None) (trouble_host host/src/fmt.rs:117)
4.789398 [INFO ] [adv] connection established (trouble_example_apps apps/src/fmt.rs:143)
4.789459 [INFO ] Updating connection parameters for connection ConnHandle(0): LeConnUpdate("LeConnUpdateParams" { "handle": ConnHandle(0), "conn_interval_min": Duration(12), "conn_interval_max": Duration(12), "max_latency": 25, "supervision_timeout": Duration(600), "min_ce_length": Duration(0), "max_ce_length": Duration(0), }) (trouble_host host/src/fmt.rs:143)
4.790130 [ERROR] [set_conn_params] error: "BleHost(Hci(Unsupported Feature or Parameter Value))" (trouble_example_apps apps/src/fmt.rs:169)
4.791076 [INFO ] [custom_task] notifying connection of tick 1 (trouble_example_apps apps/src/fmt.rs:143)
4.791137 [INFO ] [custom_task] RSSI: 127 (trouble_example_apps apps/src/fmt.rs:143)
4.908752 [WARN ] [host] unsupported l2cap channel id 58 (trouble_host host/src/fmt.rs:156)
4.908782 [WARN ] [host] encountered error processing ACL data for ConnHandle(0): NotSupported (trouble_host host/src/fmt.rs:156)
4.936218 [DEBUG] exchange_att_mtu: 185, current default: 247 (trouble_host host/src/fmt.rs:130)
4.936279 [INFO ] [host] agreed att MTU of 185 (trouble_host host/src/fmt.rs:143)
6.791198 [INFO ] [custom_task] notifying connection of tick 2 (trouble_example_apps apps/src/fmt.rs:143)
6.791259 [INFO ] [custom_task] RSSI: -55 (trouble_example_apps apps/src/fmt.rs:143)
8.791320 [INFO ] [custom_task] notifying connection of tick 3 (trouble_example_apps apps/src/fmt.rs:143
by running:
cargo run --release --features nrf52840 --target thumbv7em-none-eabihf --bin ble_bas_peripheral_sec --features="security"
The parameter LeConnUpdateParams printed is correct, but the connection parameter setting always fails due to Unsupported Feature or Parameter Value.
When using TrouBLE as central, setting connection parameter with the same value can be successful. The error only appears when the TrouBLE acts as BLE peripheral.
I also tried esp32, no error for esp32.
Do you have any ideas?
Thanks for the reproducer! I will give it a spin soon, I'm a bit busy with other things this week (and maybe the next!).
It might be related to having to poll the controller for events before issuing a command, I'm not sure.
I think I finally figure it out.
According to Accessory Design Guidelines for Apple Devices
The accessory should request connection parameters appropriate for its use case by sending an L2CAP Connection Parameter Update Request at the appropriate time. See the Bluetooth 4.0 specification, Volume 3, Part A, Section 4.20 for details
In current implementation, only LeConnUpdate is called when updating connection parameters, which is fine for centrals, but for peripherals, it might not be supported(?).
It seems that nrf-sdc doesn't support Connection Parameters Request procedure, see this devzone qa
So, according to https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/low-energy-controller/link-layer-specification.html#UUID-caad3e58-fdc5-5274-c856-04f0b711ec98, the work around is to use L2CAP LE Signaling channel to send L2CAP_CONNECTION_PARAMETER_UPDATE_REQ instead.
Now I have no idea about how to implement this, would you give me a pointer? Thanks
Interesting find! You can use https://github.com/embassy-rs/trouble/blob/main/host/src/host.rs#L498 to send an l2cap signal packet (probably makes sense to do that inside the connection.rs or host.rs in some utility fn that checks the supported features). To encode the signal packet, you define a type and implement the trait, see here: https://github.com/embassy-rs/trouble/blob/main/host/src/types/l2cap.rs#L126
The logic should probably use these commands to check what features are supported by the connection first:
https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host-controller-interface/host-controller-interface-functional-specification.html#UUID-3ad76ac5-3812-cca4-a4f5-f73e96cebcba
https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host-controller-interface/host-controller-interface-functional-specification.html#UUID-86223376-28a9-e454-15f2-e420aee8c462