Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,35 @@ venv/
# Docs
docs/node_modules/
docs/dist/

# ZMK / Zephyr build artifacts
firmware/.west/
firmware/build/
firmware/zephyr/
firmware/modules/
firmware/optional/
firmware/zmk-firmware/
firmware/.config
firmware/*.tar.xz
firmware/*.7z
firmware/zmk/app/
firmware/zmk/docs/
firmware/zmk/schema/
firmware/zmk/.clang-format
firmware/zmk/.devcontainer/
firmware/zmk/.gitattributes
firmware/zmk/.github/
firmware/zmk/.gitignore
firmware/zmk/.gitlint
firmware/zmk/.pre-commit-config.yaml
firmware/zmk/.release-please-manifest.json
firmware/zmk/*.md
firmware/zmk/AUTHORS
firmware/zmk/CODEOWNERS
firmware/zmk/LICENSE
firmware/zmk/release-please*
zmk-workspace/

# Zephyr SDK
gnu-arm-embedded/
zephyr-sdk/
21 changes: 21 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# RelayKeys Agents Documentation

## Current State: ZMK Bridge Integration (Phase 1)

**Overview**
The codebase has been updated to support a pure software-driven nRF52840 BLE HID bridge using ZMK firmware, replacing the legacy hardware-mod approach. The host side is managed by the RelayKeys Go daemon.

**Completed Work**
1. **ZMK Firmware Structure**: A `firmware/zmk` directory was added containing `west.yml`, a dummy `relaykeys_dongle` shield (bypassing hardware matrix scanning), and `relaykeys_handler.c` which implements a ZMK Studio RPC subsystem to receive binary payloads and inject HID reports over the BLE endpoint.
2. **Protobuf Integration**: Defined `relaykeys.proto` under `internal/zmk/proto` and generated Go bindings for communication between the Go daemon and ZMK firmware.
3. **Go Transport Layer**: Added `internal/zmkbridge` which frames Protobuf payloads using ZMK's SLIP/COBS-like framing (0xAB, 0xAC, 0xAD) and writes raw bytes to the serial interface.
4. **Configuration**: Added `FirmwareType` to `config.go` and `rpc/server.go`, allowing backward compatibility with `legacy` AT commands or switching to the new `zmk` protocol.

## Next Steps (Phase 2 & Beyond)

* **Implement Mouse & Consumer Reports in Firmware**: Currently `relaykeys_handler.c` only constructs and injects Keyboard reports (`zmk_hid_keyboard_report_body`). It needs to be extended to support Mouse and Consumer (media) reports based on the type enum from the protobuf definition.
* **Implement Mouse/Media Send Functions in Go**: Implement `SendMouseMove`, `SendMouseButton` and other functionalities inside `internal/zmkbridge/bridge.go` to wrap and send correct Protobuf messages.
* **Native ZMK Administration**: Map BLE bonding and profile status from ZMK back to the Go daemon state. Add protobuf RPC definitions for getting connected devices, clearing slots, and switching devices (e.g. implementing `ProcessBleCmd` completely). Update the Web UI to show which of the 5 ZMK slots are occupied and which devices are connected.
* **Compile Firmware**: Test the standard ZMK build locally or via GitHub actions for the `relaykeys_dongle` shield.

*When modifying the ZMK handler code, make sure to consider `nanopb` struct generation if new fields are added.*
16 changes: 11 additions & 5 deletions cmd/relaykeys-daemon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func runDaemon(cfg *config.Config) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

rpcServer := rpc.NewServerWithConfig(nil, cfg.Username, cfg.Password)
rpcServer := rpc.NewServerWithConfig(nil, cfg.Username, cfg.Password, cfg.FirmwareType)

exePath, _ := os.Executable()
exeDir := filepath.Dir(exePath)
Expand Down Expand Up @@ -177,11 +177,17 @@ func connectSerial(ctx context.Context, cfg *config.Config, rpcServer *rpc.Serve
}
}

if err := hwPort.Init(); err != nil {
log.Printf("Serial init warning: %v", err)
if cfg.FirmwareType != "zmk" {
if err := hwPort.Init(); err != nil {
log.Printf("Serial init warning: %v", err)
}
} else {
hwPort.Flush()
}
if err := blehid.InitSerial(hwPort); err != nil {
log.Printf("BLE HID init warning: %v", err)
if cfg.FirmwareType != "zmk" {
if err := blehid.InitSerial(hwPort); err != nil {
log.Printf("BLE HID init warning: %v", err)
}
}

log.Println("Serial device connected")
Expand Down
3 changes: 3 additions & 0 deletions firmware/zmk/boards/shields/relaykeys_dongle/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
zephyr_library()
zephyr_library_sources(relaykeys_handler.c)
zephyr_library_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../include)
2 changes: 2 additions & 0 deletions firmware/zmk/boards/shields/relaykeys_dongle/Kconfig.shield
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
config SHIELD_RELAYKEYS_DONGLE
def_bool $(shields_list_contains,relaykeys_dongle)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_USB_DEVICE_STACK=y
CONFIG_USB_CDC_ACM=y
CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=y
CONFIG_UART_LINE_CTRL=y
CONFIG_ZMK_BLE=y
CONFIG_ZMK_POINTING=y
CONFIG_ZMK_HID_CONSUMER_REPORT_USAGES_FULL=y
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/bt.h>

/ {
keymap {
compatible = "zmk,keymap";

default_layer {
bindings = <
&none
>;
};
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/ {
chosen {
zmk,kscan = &kscan0;
zmk,matrix_transform = &default_transform;
relaykeys,uart = &relaykeys_cdc;
};

default_transform: keymap_transform_0 {
compatible = "zmk,matrix-transform";
columns = <1>;
rows = <1>;
map = <0>;
};

kscan0: kscan_0 {
compatible = "zmk,kscan-mock";
columns = <1>;
rows = <1>;
events = <>;
};
};

&zephyr_udc0 {
relaykeys_cdc: relaykeys_cdc {
compatible = "zephyr,cdc-acm-uart";
};
};
Loading