-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(battery): Split battery reporting over BLE GATT #1243
Conversation
I just realized I can also share the test app relatively easily: https://cdpn.io/pen/debug/ExodqEG?authentication_hash=bYAdyOGvRaRk Clicking on |
This link expired, could you send the non-debug link? Are we aware of any operating systems that utilize two BAS services? Conversely, does this change still work on OSs that don't? It would be nice to test this on a real OS that can show the two battery levels. |
@Nicell I hosted it here: https://zmk-split-battery-report.herokuapp.com/ (JS playgrounds usually run the app in an iframe which can not access the Bluetooth API, so I tried to share the debug link instead). As I said, I have only access to macOS Monterey which still displays only one battery, the central one. This at least means it doesn't break existing battery level reporting, but to be honest, I am not sure if it's a macOS limitation or a bug in my implementation, since Apple Airpods have proper battery level reporting: https://support.apple.com/en-us/HT207012#status. But of course, Airpods might be treated specially, I will try to investigate it further. #764 claims that Windows 11 don't support it either, I have no info about linux unfortunately. |
Apple has its own peripheral communication protocol that allows for multiple battery level reporting. I know my Sony earbuds support it. |
Which is probably proprietary and we don't get the chance to implement it (without some certification). But I still think this PR makes sense even with zero OS support, we could create small apps which would display the battery level in system tray/menu, or is this overkill? |
Actually it's pretty well-documented: https://developer.apple.com/accessories/Accessory-Design-Guidelines.pdf Problem is, it's for devices using the hands-free profile (using HFP AT commands), so it's of little use for HID-over-GATT devices like keyboards. |
@tadfisher You're right, I was not aware of that. |
@Nicell Based on the discussion above, I think the best is to assume that no OS is supporting this at the moment (and to be honest, it's unlikely that they ever will be) but, as I said, it is possible to create some utility which can display the battery level (and other things as well) of both sides. I know it's not ideal, but I think it's feasible. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this! I haven't had a chance to fully review all the details, but adding a few high level questions/comments.
ZMK_EVENT_DECLARE(zmk_battery_state_changed); | ||
|
||
struct zmk_peripheral_battery_state_changed { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm.... We already have the concept of position events having a "source" (see app/include/zmk/events/position_state_changed.h
). If we're now going to have yet another event that is either from local or peripheral device, we might want to pull that out in a more generalized place, and simply supplement the existin battery state changed event w/ a source
field as well. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, I need some time to understand to how that's implemented/used.
app/src/battery_split.c
Outdated
BT_GATT_CHARACTERISTIC(BT_UUID_BAS_BATTERY_LEVEL, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY, | ||
BT_GATT_PERM_READ, read_blvl, NULL, &last_state_of_peripheral_charge), | ||
BT_GATT_CCC(blvl_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), | ||
BT_GATT_CUD("Peripheral", BT_GATT_PERM_READ)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we prepare to support multiple peripherals (see #1282 ) I think we might want to get that in first, and then work on getting this to runtime generate all the necessary characteristics based on how many configured peripherals there are?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I saw that and came to the same conclusion. Anyways, I need to create the utility which can display it meaningfully, currently I only have the test project based on the Bluetooth Web API :)
app/CMakeLists.txt
Outdated
target_sources(app PRIVATE src/behaviors/behavior_bt.c) | ||
target_sources(app PRIVATE src/ble.c) | ||
target_sources(app PRIVATE src/hog.c) | ||
if (CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We moved to the more agnostic CONFIG_ZMK_SPLIT_ROLE_CENTRAL
now:
if (CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) | |
if (CONFIG_ZMK_SPLIT_ROLE_CENTRAL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
app/Kconfig
Outdated
@@ -157,6 +156,13 @@ config BT_PERIPHERAL_PREF_LATENCY | |||
config BT_PERIPHERAL_PREF_TIMEOUT | |||
default 400 | |||
|
|||
if ((!ZMK_SPLIT_ROLE_CENTRAL) || (!ZMK_SPLIT)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe there is a cleaner way to write this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Depends on your definition of 'cleaner', but you could also do this
if (!(ZMK_SPLIT && ZMK_SPLIT_ROLE_CENTRAL))
or just remove the parenthesis around the two terms
if ( !ZMK_SPLIT || !ZMK_SPLIT_ROLE_CENTRAL )
is pretty clean
Awesome! Would it be possible to make it as a SwiftBar plugin? |
@Steve-Fan probably yes, but as I said it's just a POC at the moment :) |
@Katona Great! What API to use to get the battery info in MacOS? I'd love do some test. |
@Steve-Fan I am using the Core Bluetooth API at the moment, but it's not too convenient in my opinion. I plan to use RxBluetoothKit, that seems better. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple other items I noticed.
Hi sir, there is a bug i would like to report. When I build the firmware for Corne keyboard, I can't build for the main side when the OLED feature is enabled. it gives error : "battery_status.c:61: undefined reference to ` bt_bas_get_battery_level ' ". The slave side built normally. |
Yeah, this makes sense. We'll need a solution for a reasonable API abstraction for the consumers to show battery state, like for the display widget. |
@kien242 Thanks for reporting it! I think I was able to fix this, but I don't have the necessary hardware to test it. @petejohanson is there way to test things when the required hardware is not available? |
@Katona, Thank you for fixing that bug. One wish of mine is probably a bit annoying to you, but Can you make a widget to display the slave's battery on the master's screen? I really wish there was that widget for a split keyboard with only one screen on the main side. |
@kien242 Actually, I am working on making the display superfluous altogether :) Maybe it's just me, but I think there is no real use case of the screen, apart from maybe debugging. |
@Katona, yes sir, but It seems that the widget is not working as expected, when I try with lily58's firmware, the OLED screen always says the battery capacity is 0 |
Can confirm it works like a charm on macOS 12, using a Ferris Sweep + nice!nano v2.0 x 2. |
Yup, can confirm this working well on linux as well. problem is blueman also ignores the additional battery report. If anyone is planning on using this code and rebasing on zmk master, I recommend creating a new branch from zmk master and manually patching any edited files. there are some conflicting changes in this branch which prevent a clean rebase. You will experience horrible cmake errors if you just try to rebase. |
The feature is a must-have, thanks for this PR @Katona! Having dedicated displays for battery feels like a complete waste of energy. Are we waiting for #836 to be merged before this PR can go further? If anyone is looking for a Windows app, I made one based on Katona's POC: https://github.com/Fukkei/zmk-split-battery |
Is this PR still in the pipeline to be added to the next ZMK release? I would love to have this feature available in the firmware by default (happy ZMK user with a Ferricy here) |
@snprajwal The plan is/was to wait #836 to be merged first, then we can proceed with this. I am trying to keep this up-to-date with main, though. |
In split_central_update_indicators_callback, it seems that the peripheral is sometimes considered connected before the GATT characteristics have been discovered. If this is the case, the update_hid_indicators handle will not yet be set, and must not be written to.
In split_central_update_indicators_callback, it seems that the peripheral is sometimes considered connected before the GATT characteristics have been discovered. If this is the case, the update_hid_indicators handle will not yet be set, and must not be written to.
In split_central_update_indicators_callback, it seems that the peripheral is sometimes considered connected before the GATT characteristics have been discovered. If this is the case, the update_hid_indicators handle will not yet be set, and must not be written to.
In split_central_update_indicators_callback, it seems that the peripheral is sometimes considered connected before the GATT characteristics have been discovered. If this is the case, the update_hid_indicators handle will not yet be set, and must not be written to.
In split_central_update_indicators_callback, it seems that the peripheral is sometimes considered connected before the GATT characteristics have been discovered. If this is the case, the update_hid_indicators handle will not yet be set, and must not be written to.
In split_central_update_indicators_callback, it seems that the peripheral is sometimes considered connected before the GATT characteristics have been discovered. If this is the case, the update_hid_indicators handle will not yet be set, and must not be written to.
In split_central_update_indicators_callback, it seems that the peripheral is sometimes considered connected before the GATT characteristics have been discovered. If this is the case, the update_hid_indicators handle will not yet be set, and must not be written to.
Now that #836 has been merged, what is the plan for this PR? Would love to have this feature! |
@Babbie I am planning to update the branch or or create a new PR depending on how big the changes are. |
@Katona Any chance you've got time to work on addressing the review comments and merge conflicts on this? If not, will you be upset if I lend a hand? |
@petejohanson I'll try to do it in the next few days, I'll let you know if I can't. |
I added a zmk behavior in my fork to output the battery level of both sides, which ungracefully provides a way to view the peripheral battery level without relying on host software. 😂 |
@petejohanson Sorry but I won't be able to finish this in the near future, so you can take this over. |
b47fe6a
to
05f7cd1
Compare
05f7cd1
to
b47fe6a
Compare
* Add ability to fetch and report peripheral battery levels on split centrals. * Add additional support for adding a new Battery Level service to split centrals that exposes fetched peripheral battery levels to connected hosts. Co-authored-by: Peter Johanson <[email protected]>
b47fe6a
to
e834a9e
Compare
It is great to see that feature pushed into main branch. |
This PR implements #764.
The changes are relatively simple:
What I tested
I tested only on my wireless Corne. For the test I used the Web Bluetooth API since that was the easiest to setup for me. Here is a link to a video demonstrating it.
What I didn't test
As indicated in #764 the OSes doesn't seem to be prepared to devices with more than one battery (macOS certainly not), but not all is lost in my opinion. I still believe this PR is useful since: