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

Add wired split support using UART communication #1117

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

megamind4089
Copy link
Contributor

@megamind4089 megamind4089 commented Feb 4, 2022

Initial wired split support. Raised this PR to get feedback on initial changes and design

Changes:

  • Updated Kconfig to allow configuring split communication type using Kconfig choice
  • Type can either be BLE or SERIAL for now
  • BLE is selected as default when board supports BLE
  • Serial is selected when there is only USB support
  • Used choice for central/peripheral instead of using single flag. This is experimental. Want to get feedback on this
  • Use chosen node to select UART dev for serial split driver
  • Serial central & service behaves similarly to the current ble split comm
  • Only addition is the CRC to split position packet which i added as a defensive check
  • Please look at the a_dux Kconfig.defconfig changes and let me know whether it makes sense to use like this.

Note:

  • I tried to make changes to be not intrusive, i.e does not change/affect existing design to make PR merge smoother
  • Ignore the west.yml change. Cherrypicked one PR from zephyr main for STM32 single wire UART and will revert it
  • Ignore the stemcell.overlay file. I added this to give an idea about the change needed. Will remove this later.
  • Will update other split keyboard's defconfig once the design seems acceptable

SteMCell board config:
https://github.com/megamind4089/zmk-config/tree/main/config/boards/arm/stemcell

Cherrypicked zephyr PR:
zephyrproject-rtos/zephyr#40525

Copy link
Contributor

@petejohanson petejohanson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First pass w/ some initial questions. Thanks for the work on this!

# ZMK_BLE
endif

if ZMK_USB
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this really needs to be behind this condition, TBH.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean we can remove this check ?
I added this check for cases where we have controllers with chips without having USB peripheral like nrf52832 chip.
If we are making an assumption that we support only USB controllers, we can remove this.
Let me know if i mis-understood the question

@@ -1,19 +1,41 @@
# Copyright (c) 2021 The ZMK Contributors
# SPDX-License-Identifier: MIT

if SHIELD_A_DUX_LEFT || SHIELD_A_DUX_RIGHT

if ZMK_BLE || ZMK_USB
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't this just defaulted on? Why behind this check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, does not makes much sense. No idea why i added this.
Will remove this check.

status = "okay";
};

&kscan0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would the pins for a kscan device need overriding for just one board?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Existing split keyboards use any one of four ports D0, D1, D2 and D3 for split communication
Ex: Lily58 uses D2 (promicro RX pin), A_Dux uses D1 (I2C SDA pin) etc

STM32 chips support half duplex UART comm only on TX pins.
So STeMCell has jumpers on back side to swap the TX/RX pins.
This depends completely on the shield which uses the pin for communication.

Other way is to use some KConfig variable to detect and swap pins.
But i have not figured out how to do it exactly using Kconfig and device tree yet,

So in the meantime, i have just juggling the pins inside the shields directly.

@@ -141,12 +142,12 @@ int send_position_state() {
return 0;
}

int zmk_split_bt_position_pressed(uint8_t position) {
int zmk_split_position_pressed(uint8_t position) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It this is common, it shouldn't, IMHO, be duplicated for both, but should really be a reusable common core that then delegates to the transport to send the state.

#include <zmk/events/position_state_changed.h>
#include <zmk/matrix.h>

#if DT_HAS_CHOSEN(zmk_split_serial)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we don't have a chosen, we should just #error instead of wrapping this all in a conditional.

Comment on lines 28 to 31
#define CONFIG_ZMK_SPLIT_SERIAL_CENTRAL_POSITION_QUEUE_SIZE 5

#define CONFIG_SERIAL_THREAD_STACK_SIZE 128
#define CONFIG_SERIAL_THREAD_PRIORITY 5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not actual Kconfig settings?

uart_ready = 1;
LOG_DBG("UART device:%s ready", serial_dev->name);

while(true) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to hard loop here? Can't we just enable the RX here, and then in the notify callback, do more? Not familiar w/ the UART API in detail, but this seems off.


#include <zmk/matrix.h>

#if DT_HAS_CHOSEN(zmk_split_serial)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto, should #error if no chosen node, not wrap all this in the #if.

}

for (int i=0; i < len; i++) {
uart_poll_out(serial_dev, data[i]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not uart_tx for the whole buffer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have done the TX and RX part first using sync mode and then updated RX to async using DMA.
Will update TX also to use same apis

Comment on lines +13 to +14
remote: megamind4089
revision: v2.5.0+zmk-fixes-stm32f4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anything here actually relevant to this work that this is needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, please ignore this file. Will remove this before final cleanup.
Just added here to show that i had to cherry-pick this PR to make it work in half duplex mode.
zephyrproject-rtos/zephyr#40525

@@ -45,7 +45,7 @@ target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed
target_sources_ifdef(CONFIG_USB app PRIVATE src/events/usb_conn_state_changed.c)
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c)
if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL OR CONFIG_ZMK_SPLIT_SERIAL_ROLE_CENTRAL)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the refactor in #1314 which will massively simplify things drastically and avoid this change being needed.

@@ -155,10 +155,14 @@ config ZMK_SPLIT

if ZMK_SPLIT

choice ZMK_SPLIT_TYPE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto on #1314

Comment on lines +14 to +17
if ZMK_BLE
config ZMK_SPLIT_BLE_ROLE_CENTRAL
default y
endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be easier w/ the agnostic role config in #1314

@marcoster
Copy link

Should really the Async-API of zephyr's uart be used here?
As far as I've seen the API is only supported by STM32, NRF and sam0.
Or is the API the future 'standard' for uart in zephyr.

At least for now I'm not able to 'simply' use this on a Pi Pico... maybe I'll looker deeper on how the async api has to be implemented or find a workaround for this.

Maybe at least a Kconfig error should be generated if SERIAL_SUPPORT_ASYNC of a board is set to n?

@petejohanson
Copy link
Contributor

I do think using the async API is probably preferred, which means we should work on the RP2040 UART driver for Zephyr to add support for it.

@marcoster
Copy link

Yeah, thought so... maybe I'll find some time soon. So for now I'll have to stick with KMK for my KB2040 / Sofle combo ;-)

@mbrgm
Copy link

mbrgm commented Mar 1, 2023

@marcoster Can this be used for two Nice!Nanos in its current state?

@petejohanson What would have to be done to move this forward?

@nov1n
Copy link

nov1n commented Mar 23, 2023

I would also be interested in the answers to @mbrgm 's questions. Also, I wonder what the main benefits are of wiring the two halves. Does it reduce power consumption or improve latency?

And would it be possible to connect with Bluetooth to the host, and use uart between halves, or would this only be supported when connected over USB?

@marcoster
Copy link

@mbrgm Sorry, never wrote an answer - as I don't have any nice!nanos around I don't really know, but since the nRF52840 in Zephyr supports async uart it normally should work.
@nov1n Well I kinda don't need wireless split and some quite cheap and available controller-options (RP2040 boards i.e.) don't have any wireless capabilities.
If done right I guess it should be possible to connect to the host per BLE.

@jesseleite
Copy link

Am I right to assume that UART support in Zephyr would allow for wired split communication between peripheral and central over TRRS? 🙏

@petejohanson
Copy link
Contributor

Am I right to assume that UART support in Zephyr would allow for wired split communication between peripheral and central over TRRS? 🙏

This PR definitely needs a rebase into latest ZMK main, but yes*.

  • this assumes a proper hardware uart, not any single wire bit-banging approach, etc as is often used in many three wire TRRS splits.

@M1cha
Copy link

M1cha commented Sep 20, 2023

I have a corne cherry with sparkfun_pro_micro_rp2040 and will make this work with the latest main branch within the next few days. Shall I open a new PR once done?

@petejohanson
Copy link
Contributor

I have a corne cherry with sparkfun_pro_micro_rp2040 and will make this work with the latest main branch within the next few days. Shall I open a new PR once done?

Sure! See my previous note about the current state needing a working UART device (in terms of Zephyr device w/ associated UART driver) so any single wire work will need to either:

  • Adapt this code to use a higher level abstraction than the Zephyr UART driver one, OR
  • Create a UART driver that works for single wire hardware, maybe using PIO, etc. Doing this likely requires experimental Zephyr version that recently added PIO driver support.

@M1cha M1cha mentioned this pull request Oct 5, 2023
3 tasks
@jonathanfv
Copy link

Hey there, I was looking at implementing "just this" (wired connection between halves, likely using TRRS and UART TTL) on the Glove80 (dual nRF52840, harware UART). Any way I can help?

@thomasbaart
Copy link

Am also interested in seeing this happen. If there's a contributor who wants to pick this up but needs hardware to do so, please post a comment and I'll sponsor the needful :)

DavidMirror added a commit to DavidMirror/zmk-config that referenced this pull request Apr 12, 2024
Update Apr 12 2024, my Dilemma is wired and I've learned that ZMK does not currently support wired splits. Without wanting to get new microcontrollers, I've decided to either wait this out or pivot to a Sugar Glider or Reviung 41. Currently following [lesshonor's Mechhwild ZMK work](https://github.com/lesshonor/mechwild-zmk-keyboards/tree/cirque) and [PR 1117 of ZMK](zmkfirmware/zmk#1117)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants