zmk icon indicating copy to clipboard operation
zmk copied to clipboard

feat(battery): Split battery reporting over BLE GATT

Open Katona opened this issue 3 years ago • 21 comments

This PR implements #764.

The changes are relatively simple:

  • the central registers on battery level changes of peripheral over BLE GATT.
  • then it 'relays' the peripheral battery level changes to the host using BLE GATT again. :)
  • for this to work, the central no longer relies on the Battery Service of Zephyr since that assumes one battery only. Instead, it defines its own BAS with two battery level characteristics.

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

  • I didn't test non split BLE keyboards since I don't have any.
  • Tested only using macOS. I don't know how other OSes display battery level when multiple one are present in one device.

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:

  • it is actually standard (the BLE standard allows it).
  • it is not breaking anything: OSes (macOS) ignore the second battery, so battery level reporting still works for central and, eventually OSes might support it (not likely, though).
  • we can develop utilities which can display both batteries in the system tray or menu.

Katona avatar Apr 13 '22 16:04 Katona

I just realized I can also share the test app relatively easily: https://cdpn.io/pen/debug/ExodqEG?authentication_hash=bYAdyOGvRaRk

Clicking on Pair Device will bring up a window where you have to select Corne from the list (I have to switch to a new profile to appear there) then pair. After that you can check the logs in the developer console (F12 on Chrome) where the battery level change events should be displayed.

Katona avatar Apr 15 '22 12:04 Katona

I just realized I can also share the test app relatively easily: https://cdpn.io/pen/debug/ExodqEG?authentication_hash=bYAdyOGvRaRk

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 avatar Apr 15 '22 17:04 Nicell

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

Katona avatar Apr 15 '22 20:04 Katona

Apple has its own peripheral communication protocol that allows for multiple battery level reporting. I know my Sony earbuds support it.

tadfisher avatar Apr 16 '22 01:04 tadfisher

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?

Katona avatar Apr 16 '22 11:04 Katona

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 avatar Apr 17 '22 00:04 tadfisher

@tadfisher You're right, I was not aware of that.

Katona avatar Apr 17 '22 21:04 Katona

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

Katona avatar Apr 19 '22 07:04 Katona

Making this PR more attractive: image

I created a (very) POC macOS app which displays the battery levels in the menubar.

Katona avatar Jun 24 '22 15:06 Katona

Making this PR more attractive: image

I created a (very) POC macOS app which displays the battery levels in the menubar.

Awesome! Would it be possible to make it as a SwiftBar plugin?

zhenchaopro avatar Jun 29 '22 16:06 zhenchaopro

@steve-fan probably yes, but as I said it's just a POC at the moment :)

Katona avatar Jun 30 '22 16:06 Katona

@Katona Great! What API to use to get the battery info in MacOS? I'd love do some test.

zhenchaopro avatar Jul 01 '22 02:07 zhenchaopro

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

Katona avatar Jul 01 '22 07:07 Katona

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.

kien242 avatar Jul 20 '22 10:07 kien242

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.

petejohanson avatar Jul 20 '22 15:07 petejohanson

@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 avatar Jul 20 '22 19:07 Katona

@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 avatar Jul 21 '22 03:07 kien242

@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 avatar Jul 21 '22 21:07 Katona

@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

kien242 avatar Jul 23 '22 03:07 kien242

Can confirm it works like a charm on macOS 12, using a Ferris Sweep + nice!nano v2.0 x 2.

abonham avatar Aug 05 '22 03:08 abonham

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.

WarpspeedSCP avatar Aug 18 '22 06:08 WarpspeedSCP

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 https://github.com/zmkfirmware/zmk/pull/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

Maksim-Isakau avatar Nov 13 '22 23:11 Maksim-Isakau

@Fukkei thanks! Yes we are waiting for #836.

Katona avatar Nov 14 '22 17:11 Katona

Tested on Windows. Works with @Katona PR and @Fukkei WindowsApp

image

equiman avatar Jan 17 '23 06:01 equiman

Sadly, I reverted the PR on my repo because &kp SEMICOLON was acting as &kp COLON.

Repo without PR: https://github.com/deinsoftware/zmk/tree/main (same as zmk last version) Repo with PR: https://github.com/deinsoftware/zmk/tree/battery

Once I reverted the changes, works again as expected. This is the morph that is failing.

mp_cs: morph_dev_comma_semi {
    compatible = "zmk,behavior-mod-morph";
    label = ", ;";
    #binding-cells = <0>;
    mods = <(MOD_LSFT|MOD_RSFT)>;
    bindings = <&kp COMMA>, <&kp SEMICOLON>;
};

This is my ZMK configuration that is failing: https://github.com/deinsoftware/swept-zmk-config/tree/v3.4.0

Let me know if I can help with more information

equiman avatar Jan 17 '23 22:01 equiman

Sadly, I reverted the PR on my repo because &kp SEMICOLON was acting as &kp COLON.

This is because your branch is missing the mod-morph commit that added mod masking by default: https://github.com/deinsoftware/zmk/commit/ef2e6e9156806760d9e91c0d635cd809058c3ee7

You could probably merge this PR branch into current ZMK main (rather than directly using it) and that would resolve it.

caksoylar avatar Jan 18 '23 06:01 caksoylar

@equiman Updated this branch with main, you can try if you want. Please note that this is still a POC.

Katona avatar Jan 18 '23 10:01 Katona

+1, works amazingly on macos 13.1 with nice nano v2s

gaoDean avatar Jan 19 '23 03:01 gaoDean

Works again as expected.

image

Thanks @caksoylar and @Katona

equiman avatar Jan 19 '23 17:01 equiman

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 avatar Apr 06 '23 19:04 snprajwal