Skip to content
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(ble): Support peripheral battery levels. #2045

Merged
merged 1 commit into from
Jan 3, 2024

Conversation

petejohanson
Copy link
Contributor

@petejohanson petejohanson commented Nov 29, 2023

  • 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.

This is a continuation of the great work from @Katona in #1243 with some important changes:

  • Move both central fetching and reporting of peripheral battery levels behind new (documented) Kconfig flags
  • After reviewing the Battery Level Service specification, it became clear to me there could be an easier structure for this. In particular, that spec lays out the requirements if you're going to have more that one battery level service, how to tag a particular battery level characteristic as the main one, etc. Given that, this PR:
    • Simplifies the core battery reporting by simply leveraging the normal Zephyr BAS directly still, basically everywhere, including splits of both roles. I have an open PR to Zephyr to properly tag the Zephyr BAS as the main one: bluetooth: Add CPF attribute to BAS battery level. zephyrproject-rtos/zephyr#65944 I am monitoring the response there, assuming no major concerns I will be cherry picking that one small change into our Zephyr fork.
    • Behind the extra Kconfig flag there is now a dedicated new "central BAS proxy" that adds another Battery Level service where the Battery Level characteristics are all marked with a description of auxiliary to disambiguate them from the main one. The new service properly handles multiple peripherals now as well.
  • Tweaks one default Zephyr L2CAP buffer value on splits to be sure we don't get -ENOMEM errors on reconnects to peripherals.

Many thanks to @Katona for all the work they put into this!

Testing

I've performed the following testing so far:

  • Confirmed the battery reporting for the split central main battery is the one displayed on the follow OSes:
    • Linux (GNOME, using upower, on latest Fedora release)
    • macOS
    • Windows 11
    • Android 14
  • Confirmed that using bluetoothctl I can subscribe to notifications on the other Battery Level service on a split central, and properly receive notifications it has "proxied" from the split peripheral.

The following testing would be nice:

  • iOS device
  • The custom macOS app/service that @Katona wrote.

To test, you need to enable the following off-by-default Kconfig settings:

CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING=y
CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY=y

@petejohanson petejohanson added enhancement New feature or request split bluetooth Bluetooth related items labels Nov 29, 2023
@petejohanson petejohanson self-assigned this Nov 29, 2023
@petejohanson petejohanson requested a review from a team as a code owner November 29, 2023 22:49
@petejohanson petejohanson force-pushed the split_battery_service branch 2 times, most recently from e74c976 to 05f7cd1 Compare November 29, 2023 22:56
@equiman
Copy link

equiman commented Nov 30, 2023

I've merged these changes into a branch and config the west.yml file to use it:

manifest:
  remotes:
    - name: deinsoftware
      url-base: https://github.com/deinsoftware
  projects:
    - name: zmk
      remote: deinsoftware
      revision: main
      import: app/west.yml
  self:
    path: config

And tested it with the @Maksim-Isakau zmk-split-battery app for windows. But it only shows one battery level.

image

On a previous version. It can show both:
image

Will it need some extra configuration on my corne-zmk-config?

@petejohanson
Copy link
Contributor Author

I've merged these changes into a branch and config the west.yml file to use it:

manifest:
  remotes:
    - name: deinsoftware
      url-base: https://github.com/deinsoftware
  projects:
    - name: zmk
      remote: deinsoftware
      revision: main
      import: app/west.yml
  self:
    path: config

And tested it with the @Maksim-Isakau zmk-split-battery app for windows. But it only shows one battery level.

image

It seems that the app from @Katona assumes it just needs to use the first Battery Service, I stead of aggregating all of them from the device: https://github.com/Katona/zmk-ble/blob/main/zmk-ble/ZmkPeripheral.swift#L53

It will need to get updated to work properly.

Maksim-Isakau added a commit to Maksim-Isakau/zmk-config that referenced this pull request Nov 30, 2023
@Maksim-Isakau
Copy link

And tested it with the @Maksim-Isakau zmk-split-battery app for windows. But it only shows one battery level.

@equiman fixed. Please note that you have to specify

CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING=y
CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY=y

in your KConfig file of zmk-config, split battery reporting is off by default. I stumbled upon this at first

@Maksim-Isakau
Copy link

iOS device

@petejohanson iOS 17 seems to work fine:
ios-zmk-split-batt-lvl

15% is peripheral, 49 main.
On a Lock Screen i see the one that has minimal battery level (tested after charging peripheral).

@petejohanson
Copy link
Contributor Author

And tested it with the @Maksim-Isakau zmk-split-battery app for windows. But it only shows one battery level.

@equiman fixed. Please note that you have to specify

CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_FETCHING=y
CONFIG_ZMK_SPLIT_BLE_CENTRAL_BATTERY_LEVEL_PROXY=y

in your KConfig file of zmk-config, split battery reporting is off by default. I stumbled upon this at first

Good note. I added to the Testing section of the PR description.

@petejohanson
Copy link
Contributor Author

iOS device

@petejohanson iOS 17 seems to work fine: ios-zmk-split-batt-lvl

15% is peripheral, 49 main. On a Lock Screen i see the one that has minimal battery level (tested after charging peripheral).

It's a shame they don't use our display string at all or otherwise disambiguate the two battery levels, but at least they show them both.

@equiman
Copy link

equiman commented Nov 30, 2023

Thanks @petejohanson and @Maksim-Isakau. Now it's working again.

image

It's possible to add some config labels to rename each one?

I think Peripheral #: is descriptive but not a cool name,
I would like to rename it as Left and Right for example.

@petejohanson
Copy link
Contributor Author

petejohanson commented Nov 30, 2023

Thanks @petejohanson and @Maksim-Isakau. Now it's working again.

image

It's possible to add some config labels to rename each one?

I think Peripheral #: is descriptive but not a cool name,
I would like to rename it as Left and Right for example.

I wonder if perhaps I can enhance this to add a CUD based on the keyboard name? Or we offer an additional Kconfig for the battery label, and we proxy the CUD from the connected peripherals once they are connected....

I'd probably do that as a follow up enhancement, once this core work is in.

@equiman
Copy link

equiman commented Nov 30, 2023

I like more the idea of an additional kconfig battery label.

I am thoroughly satisfied with monitoring only the battery levels for each side, regardless of the labels. I believe this enhancement would be beneficial for future improvements.

@petejohanson
Copy link
Contributor Author

Ok, so the Zephyr PR was accepted and merged, so I've cherry picked that into our Zephyr fork in zmkfirmware/zephyr#27

Waiting to see if @Katona is able to work on his macOS app bits, but I don't think that's a blocker for merging this in the meantime.

@equiman
Copy link

equiman commented Dec 1, 2023

Well, today something strange is happening. It's showing all battery levels at -1

image

Or sometimes only, show the central level:

image

@petejohanson
Copy link
Contributor Author

Well, today something strange is happening. It's showing all battery levels at -1

image

Or sometimes only, show the central level:

image

Is the main battery display in Windows working?

@equiman
Copy link

equiman commented Dec 1, 2023

Yes, it is:
image

image

@petejohanson petejohanson changed the title feat(ble): Support perhipheral battery levels. feat(ble): Support peripheral battery levels. Dec 1, 2023
@bryanforbes
Copy link

It seems like there should be an API for getting the peripheral battery level that's similar to zmk_battery_state_of_charge(). This would be useful for displaying peripheral battery level in the RGB underglow like the Glove80 does. Right now, the only place where the levels are stored is in the proxy, but it seems wrong to force users to enable the proxy just to get the levels for use elsewhere. Which means the simplest solution would be to store the levels in central.c when fetching is enabled. (cc @chrisandreae)

@petejohanson petejohanson force-pushed the split_battery_service branch 2 times, most recently from e834a9e to 398a241 Compare December 9, 2023 21:19
@petejohanson
Copy link
Contributor Author

It seems like there should be an API for getting the peripheral battery level that's similar to zmk_battery_state_of_charge(). This would be useful for displaying peripheral battery level in the RGB underglow like the Glove80 does. Right now, the only place where the levels are stored is in the proxy, but it seems wrong to force users to enable the proxy just to get the levels for use elsewhere. Which means the simplest solution would be to store the levels in central.c when fetching is enabled. (cc @chrisandreae)

Changed the storage to the central code, added a new API for fetching the levels there, and then leveraged that in the BAS proxy code instead.

@equiman
Copy link

equiman commented Dec 9, 2023

Few days ago, the keyboard stop reporting correctly the battery level, even on windows.

Always shown the level in 50%. Today it was run on battery, and report it has 50%. Once I've fully charged it, still reporting 50%.

image

And the battery status won't connect any more:

image

@equiman
Copy link

equiman commented Dec 27, 2023

I've merged the branch with the last changes, and still working on windows:

image

image

@bryanforbes
Copy link

bryanforbes commented Dec 29, 2023

I've been using the latest for roughly a day and I noticed that it seems to be reporting the right side (peripheral) battery level of my Glove80 as the main bluetooth battery level in macOS:

image image

The battery widget (first image) is correctly ordering the halves: left on top (89%), right below it (100%). But the BT menu is reporting the battery level as 100% (right half).

Update: After a while (I'm not sure how long), the BT menu started reporting the left half percentage and the battery widget switched the order of the two halves from the images I uploaded earlier (left on bottom, right on top). Could this be some sort of race condition?

@petejohanson
Copy link
Contributor Author

petejohanson commented Dec 29, 2023

I've been using the latest for roughly a day and I noticed that it seems to be reporting the right side (peripheral) battery level of my Glove80 as the main bluetooth battery level in macOS:
image image

The battery widget (first image) is correctly ordering the halves: left on top (89%), right below it (100%). But the BT menu is reporting the battery level as 100% (right half).

Update: After a while (I'm not sure how long), the BT menu started reporting the left half percentage and the battery widget switched the order of the two halves from the images I uploaded earlier (left on bottom, right on top). Could this be some sort of race condition?

Not sure it's a race condition, as much as indeterminate behavior on how macOS handles multiple battery level characteristics split over two different battery GATT services... Initial testing seemed to show that they properly picked the characteristic marked as the main.

Are you perhaps on a different underlying Zephyr branch, testing the Zephyr 3.5 work? Not sure I cherry picked a needed Zephyr fix there (will be doing that momentarily) (edit: Just cherry picked and pushed that Zephyr commit (a1bd392633e8b3c9096f6f8bdd0d0f6fd0e68780) into my Zephyr 3.5 branch, in case that was the issue)

@bryanforbes
Copy link

Are you perhaps on a different underlying Zephyr branch, testing the Zephyr 3.5 work?

No, I’m using MoErgo’s main (Zephyr 3.2) as my base and replaced their split battery reporting commit with the work here and updated the underglow status to use the new API. I’ll test this against a stock ZMK later this afternoon to try to narrow down the issue.

@petejohanson
Copy link
Contributor Author

Are you perhaps on a different underlying Zephyr branch, testing the Zephyr 3.5 work?

No, I’m using MoErgo’s main (Zephyr 3.2) as my base and replaced their split battery reporting commit with the work here and updated the underglow status to use the new API. I’ll test this against a stock ZMK later this afternoon to try to narrow down the issue.

I suspect you'll find the same thing occurring, and it's simply that the macOS selection of the battery level to display is non-deterministic.

@petejohanson
Copy link
Contributor Author

I've been using the latest for roughly a day and I noticed that it seems to be reporting the right side (peripheral) battery level of my Glove80 as the main bluetooth battery level in macOS:
image image

The battery widget (first image) is correctly ordering the halves: left on top (89%), right below it (100%). But the BT menu is reporting the battery level as 100% (right half).

Update: After a while (I'm not sure how long), the BT menu started reporting the left half percentage and the battery widget switched the order of the two halves from the images I uploaded earlier (left on bottom, right on top). Could this be some sort of race condition?

I don't think we're getting any help any time soon...

image

My gut is to not hold up our merge of this just because macOS doesn't do sane battery level characteristic selection. The new Sonoma battery widget actually shows the multiple levels. Hopefully it will do so with labels when we add the GATT CUD to each battery level.

Also, the proxy portion is optional, and not enabled by default so there's less risk of this being an issue while we continue to iterate.

@bryanforbes Any concerns?

@bryanforbes
Copy link

No concerns from me

@petejohanson petejohanson merged commit 0e2f94b into zmkfirmware:main Jan 3, 2024
46 checks passed
@dudicoco
Copy link

dudicoco commented Jan 5, 2024

I've added the parameters to my config but I do not see peripheral battery status on macOs:

Screenshot 2024-01-05 at 17 11 29

https://github.com/dudicoco/corne-wireless-zmk-config/blob/dacb95898f9254b86278ddb7112ca28b014f9655/config/corne.conf#L20-L23

@madushan1000
Copy link

here is a python script to see split battery levels on linux. https://gist.github.com/madushan1000/9744eb6350a5dd9685fb6bfbb25fbb8a

@dstudzinski
Copy link

I've been using the latest for roughly a day and I noticed that it seems to be reporting the right side (peripheral) battery level of my Glove80 as the main bluetooth battery level in macOS:

image image

The battery widget (first image) is correctly ordering the halves: left on top (89%), right below it (100%). But the BT menu is reporting the battery level as 100% (right half).

Update: After a while (I'm not sure how long), the BT menu started reporting the left half percentage and the battery widget switched the order of the two halves from the images I uploaded earlier (left on bottom, right on top). Could this be some sort of race condition?

What's your widget/app for macos? I can't find any.

@bryanforbes
Copy link

What's your widget/app for macos? I can't find any.

It’s just the battery widget in Sonoma in the notification center.

tannakartikey added a commit to tannakartikey/zmk-config that referenced this pull request Jan 31, 2024
xervon added a commit to xervon/zmk-config that referenced this pull request Feb 14, 2024
xervon added a commit to xervon/zmk-config that referenced this pull request Feb 14, 2024
@bjornborg
Copy link

bjornborg commented Mar 7, 2024

here is a python script to see split battery levels on linux. https://gist.github.com/madushan1000/9744eb6350a5dd9685fb6bfbb25fbb8a

Great script. I have fused this to another gnome extension, so it is graphically shown in gnome v.42 (ubuntu 22.04).
image
The repo is located here: https://github.com/bjornborg/gnome-bluetooth-battery-indicator/tree/gnome.42

@mh4x0f
Copy link

mh4x0f commented Mar 18, 2024

I created an app inspired by the @Maksim-Isakau project just for Linux on Dbus Bluez.

image

https://github.com/mh4x0f/zmkBATx

@dudicoco
Copy link

I've been using the latest for roughly a day and I noticed that it seems to be reporting the right side (peripheral) battery level of my Glove80 as the main bluetooth battery level in macOS:

image image
The battery widget (first image) is correctly ordering the halves: left on top (89%), right below it (100%). But the BT menu is reporting the battery level as 100% (right half).

Update: After a while (I'm not sure how long), the BT menu started reporting the left half percentage and the battery widget switched the order of the two halves from the images I uploaded earlier (left on bottom, right on top). Could this be some sort of race condition?

@bryanforbes can you please share what widget are you using for mac?

@dudicoco
Copy link

Figured out how to display the split battery status on mac:

  1. Follow the instructions on how to add a widget - https://support.apple.com/en-il/guide/mac-help/mchl52be5da5/mac.
  2. Add the battery widget.

roadhouse added a commit to roadhouse/zmk-config-corne-xiao-choc that referenced this pull request May 22, 2024
@mike1808
Copy link

here is a python script to see split battery levels on linux. https://gist.github.com/madushan1000/9744eb6350a5dd9685fb6bfbb25fbb8a

thanks! created a polybar module if anyone uses it with i3 or other wms

https://gist.github.com/mike1808/13af77145a292c627fa6508f5fb6730b
image

@rottencandy
Copy link

here is a python script to see split battery levels on linux. https://gist.github.com/madushan1000/9744eb6350a5dd9685fb6bfbb25fbb8a

Thank you, I made a bash version of this script
https://gist.github.com/rottencandy/901189a3cfac94bcbdefdf40236b042e

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bluetooth Bluetooth related items enhancement New feature or request split
Projects
None yet
Development

Successfully merging this pull request may close these issues.