Closed HaoboGu closed 1 month ago
Just did a little more investigation. This issue only appears when the host is windows.
I also notice that for macOS and iOS, after the first connection and reconnection, the on_write
of HidService
is called, because I saw the logs like
INFO HID input keyboard cccd: [1, 0]
└─ rmk::ble::nrf::hid_service::{impl#0}::on_write @ E:\rmk\rmk\src\ble\nrf\hid_service.rs:201
INFO HID input media keys: [1, 0]
└─ rmk::ble::nrf::hid_service::{impl#0}::on_write @ E:\rmk\rmk\src\ble\nrf\hid_service.rs:211
INFO HID input via keys: [1, 0]
└─ rmk::ble::nrf::hid_service::{impl#0}::on_write @ E:\rmk\rmk\src\ble\nrf\hid_service.rs:209
Then everything goes well.
When connecting to a windows host, the logs above only appear at the first connection. It seems calling on_write
indicates that everything is good. But I still don't know why connecting to windows doesn't call on_write
The NRF softdevice documentation is the best source for detailed information. They have message sequence charts and descriptions of each possible error return from each API function. In the case of sd_ble_gatts_hvx
an InvalidState
return indicates:
One or more of the following is true:
- Invalid Connection State
- Notifications and/or indications not enabled in the CCCD
- An ATT_MTU exchange is ongoing
Most likely the second option. One of the main things that's saved in the sys_attrs is the value of all CCCD descriptors. The GATT spec says that those values persist across connections for bonded pairs, which is why you're host only writes to the CCCD descriptor on the first connection. If your InvalidState
error is due to notifications not being enabled in the CCCD then you're not saving or loading the sys attrs correctly. In your SecurityHandler::save_sys_attrs
callback you need to call gatt_server::get_sys_attrs
on the connection and save the blob for that peer. Then in SecurityHandler::load_sys_attrs
you need to call gatt_server::set_sys_attrs
with the exact blob that was previously saved for that peer.
The NRF softdevice documentation is the best source for detailed information. They have message sequence charts and descriptions of each possible error return from each API function. In the case of
sd_ble_gatts_hvx
anInvalidState
return indicates:One or more of the following is true:
Invalid Connection State
Notifications and/or indications not enabled in the CCCD
An ATT_MTU exchange is ongoing
Most likely the second option. One of the main things that's saved in the sys_attrs is the value of all CCCD descriptors. The GATT spec says that those values persist across connections for bonded pairs, which is why you're host only writes to the CCCD descriptor on the first connection. If your
InvalidState
error is due to notifications not being enabled in the CCCD then you're not saving or loading the sys attrs correctly. In yourSecurityHandler::save_sys_attrs
callback you need to callgatt_server::get_sys_attrs
on the connection and save the blob for that peer. Then inSecurityHandler::load_sys_attrs
you need to callgatt_server::set_sys_attrs
with the exact blob that was previously saved for that peer.
I believe that I did this correctly. As you can see in the log, I saved the sys_attr to flash when bonding, and at the second connection, same sys_attr was loaded and set. But I still got InvalidState when the host is Windows (it works correctly for macos/iOS).
save_sys_attr was not called because I was testing the power loss situation. I'm testing cases of save_sys_attr is called.
Yeah, when save_sys_attr
is called, the correct sys_attr is saved and everything goes well. But in the power loss situation, it's not correctly saved. I added an extra save_sys_attr
call when bonding, but the value of not correct. How do i solve this?
You can call get_sys_attrs
whenever you want and update your saved data. To be power safe you'd need to do that whenever the sys_attrs change. Unfortunately, Nordic doesn't document exactly what's included in the sys_attrs, and some of the changes may not generate events that are observable by the application. At the very least you'd want to do it whenever a CCCD value is written. Bluetooth as a spec was designed for battery powered devices so I don't think power loss safety was a prime concern.
It is good to use get_sys_attrs
whenever I set sys_attrs? For example, in load_sys_attrs
, using get_sys_attrs
to replace load saved sys_attrs, before calling set_sys_attrs
,
No, that won't work. get_sys_attrs
gets the current value of the system attributes for the connection. Calling set_sys_attrs
with the result from get_sys_attrs
would be a no-op. Please review the the Nordic documentation for the sys attrs and the message sequences around setting and getting them.
At this point I think everything in nrf_softdevice
is working as intended, so I'm going to close the issue. If you want more help getting your application to work, Matrix chat is your best bet; though be aware that everyone is volunteering their time there and I don't think many people have done much work with dealing with bonded connections.
thanks!
The original issue is https://github.com/embassy-rs/nrf-softdevice/issues/237, since it's a different problem, I'm opening this new issue.
I just found I didn't really solve the reconnection problem. I called
load_sys_attrs
after connected:where my
load_sys_attrs
is:The first connection and reconnection are both good, but once I tried to send HID report, the
gatt_server::notify_value(self.conn, self.handle, report)
returns aRaw(InvalidState)
error. Here is the full log of the first and second connection:first(all good):
second(reconnection, notify value error):
And same as before, if I remove
bonder.load_sys_attrs(&conn);
, then I getRaw(BleGattsSysAttrMissing)
error.The sys_attr saved and loaded was right according to the log, but the
InvalidState
still happens when callinggatt_server::notify_value()
@alexmoon Do you have any ideas? I'll much appreciate if you can help!
Originally posted by @HaoboGu in https://github.com/embassy-rs/nrf-softdevice/issues/237#issuecomment-2131286708