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
26 changes: 23 additions & 3 deletions .github/workflows/build-nrf52.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,35 @@ jobs:
runs-on: ubuntu-22.04
strategy:
matrix:
board: ["adafruit_feather_nrf52840", "seeed_xiao_nrf52840"]
include:
- board: adafruit_feather_nrf52840
artifact_ext: uf2
- board: seeed_xiao_nrf52840
artifact_ext: uf2
- board: mdbt50q_cx_40_dongle_nrf52840
artifact_ext: zip
steps:
- uses: actions/checkout@v4
- name: Build
run: |
docker run -v $PWD:/workdir/project -w /workdir/project/firmware-bluetooth nordicplayground/nrfconnect-sdk:v2.2-branch \
west build -b ${{ matrix.board }}
cp firmware-bluetooth/build/zephyr/remapper.uf2 firmware-bluetooth/remapper_${{ matrix.board }}.uf2
- name: Package UF2
if: matrix.artifact_ext == 'uf2'
run: |
cp firmware-bluetooth/build/zephyr/remapper.uf2 \
firmware-bluetooth/remapper_${{ matrix.board }}.uf2
- name: Package DFU zip
if: matrix.artifact_ext == 'zip'
run: |
pip install nrfutil
nrfutil pkg generate \
--hw-version 52 \
--sd-req 0x00 \
--application firmware-bluetooth/build/zephyr/remapper.hex \
--application-version 1 \
firmware-bluetooth/remapper_${{ matrix.board }}.zip
- uses: actions/upload-artifact@v4
with:
name: artifact-${{ matrix.board }}
path: firmware-bluetooth/remapper_${{ matrix.board }}.uf2
path: firmware-bluetooth/remapper_${{ matrix.board }}.${{ matrix.artifact_ext }}
28 changes: 27 additions & 1 deletion BLUETOOTH.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,34 @@ The Bluetooth version of the remapper runs on Nordic's nRF52840 chip. Currently

* [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062)
* [Seeed Studio Xiao nRF52840](https://www.seeedstudio.com/Seeed-XIAO-BLE-nRF52840-p-5201.html)
* [Raytac nRF52840 Dongle](https://www.raytac.com/product/ins.php?index_id=156)

To flash the [firmware](firmware-bluetooth), first put the board in flashing mode by double clicking the reset button quickly. A drive should appear on your computer. Copy the [UF2 file that matches your board](https://github.com/jfedor2/hid-remapper/releases/latest) to that drive and that's it. If you want to flash a newer version of the firmware in the future, you can also put the board in firmware flashing mode using the HID Remapper [web configuration tool](https://www.remapper.org/config/).
## Firmware flash
To flash the [firmware](firmware-bluetooth) for **UF2 bootloader** devices (Adafruit Feather nRF52840 Express, Seeed Studio Xiao nRF52840):
1. Put the board in flashing mode by double clicking the reset button quickly. A drive should appear on your computer.
2. Copy the [UF2 file that matches your board](https://github.com/jfedor2/hid-remapper/releases/latest) to that drive and that's it.

After initial install, if you want to flash a newer version of the firmware in the future, you can also put the board in firmware flashing mode using the HID Remapper [web configuration tool](https://www.remapper.org/config/).


To flash the [firmware](firmware-bluetooth) for **Nordic DFU bootloader** devices (Raytac MDBT50Q-CX-40 Dongle):
1. Install [nrfutil](https://www.nordicsemi.com/Products/Development-tools/nrf-util) if you haven't already.
2. Hold the button and plug the dongle into a USB port. Release button after ~1 second. The LED should pulse slowly, indicating DFU mode is active.
3. Flash using one of the two methods below.

Method 1 — automatic device detection (newer nrfutil):\
`nrfutil device program --firmware remapper.zip --traits nordicDfu`\
Requires the `device` component: `nrfutil install device`

Method 2 — specify port manually (older nrfutil):\
Find your port first: Windows check Device Manager under "Ports (COM & LPT)", Linux run `ls /dev/ttyACM*`, macOS run `ls /dev/tty.usbmodem*`. Linux and macOS users may need elevated permissions. Either run with `sudo`, or add your user to the `dialout` group (Linux) / `uucp` group (macOS).\
`nrfutil dfu usb-serial -pkg remapper.zip -p /dev/ttyACM0` #Linux\
`nrfutil dfu usb-serial -pkg remapper.zip -p COM3` #Windows\
Requires the `nrf5sdk-tools` component: `nrfutil install nrf5sdk-tools`

To upgrade firmware, you'll have to follow the above steps again. The flash action on web UI does not work. Given that feature was meant for custom enclosures where the physical buttons are concealed, and this device has a readily accessible button, you should just use the physical button to perform upgrades.

## Pairing
To connect Bluetooth devices to the remapper, you need to put the device in pairing mode. This is device-specific, but usually involves holding a button for a few seconds. Then you also need to put HID Remapper in pairing mode. You do this by either pressing the "user switch" button on the board or by clicking the "Pair new device" button on the web configuration tool (the Xiao board doesn't have a user button so you have to either do it through the web interface or by shorting pin 0 to GND). The remapper will also automatically enter pairing mode if no devices are currently paired.

You can tell the remapper is in pairing mode if the blue LED is lit constantly. When it's not in pairing mode, the blue LED will be blinking, with the number of blinks per cycle corresponding to the number of currently connected devices.
Expand All @@ -25,3 +50,4 @@ To make the remapper forget all currently paired devices, hold the "user switch"

* Quirks mechanism for fixing broken report descriptors doesn't work.
* Reconnects could be faster if we cached attributes/report descriptor.
* Web UI firmware upgrade does not work for Raytac MDBT50Q-CX-40 Dongle
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ custom board v5 | [remapper\_board.uf2](https://github.com/jfedor2/hid-remapper/
custom board v6 | [remapper\_board.uf2](https://github.com/jfedor2/hid-remapper/releases/latest/download/remapper_board.uf2) | disconnect and reconnect after flashing
custom board v7 | [remapper\_board\_v7.uf2](https://github.com/jfedor2/hid-remapper/releases/latest/download/remapper_board_v7.uf2) | disconnect and reconnect after flashing
custom board v8 | [remapper\_board\_v8.uf2](https://github.com/jfedor2/hid-remapper/releases/latest/download/remapper_board_v8.uf2) |
Feather nRF52840 Express | [remapper_adafruit_feather_nrf52840.uf2](https://github.com/jfedor2/hid-remapper/releases/latest/download/remapper_adafruit_feather_nrf52840.uf2) |
Xiao nRF52840 | [remapper_seeed_xiao_nrf52840.uf2](https://github.com/jfedor2/hid-remapper/releases/latest/download/remapper_seeed_xiao_nrf52840.uf2) |
Feather nRF52840 Express | [remapper_adafruit_feather_nrf52840.uf2](https://github.com/jfedor2/hid-remapper/releases/latest/download/remapper_adafruit_feather_nrf52840.uf2) | see [BLUETOOTH.md](BLUETOOTH.md) for more details
Xiao nRF52840 | [remapper_seeed_xiao_nrf52840.uf2](https://github.com/jfedor2/hid-remapper/releases/latest/download/remapper_seeed_xiao_nrf52840.uf2) | see [BLUETOOTH.md](BLUETOOTH.md) for more details
Raytac MDBT50Q-CX Dongle | [remapper_mdbt50q_cx_40_dongle_nrf52840.zip](https://github.com/jfedor2/hid-remapper/releases/latest/download/remapper_mdbt50q_cx_40_dongle_nrf52840.zip) | see [BLUETOOTH.md](BLUETOOTH.md) for more details
serial | [remapper_serial.uf2](https://github.com/jfedor2/hid-remapper/releases/latest/download/remapper_serial.uf2) |

For boards not listed above, use the same file name you used when flashing it for the first time.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: Apache-2.0

zephyr_library()
zephyr_library_sources(board.c)
7 changes: 7 additions & 0 deletions firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: Apache-2.0

config BOARD_ENABLE_DCDC
bool "DCDC mode"
select SOC_DCDC_NRF52X
default y
depends on BOARD_MDBT50Q_CX_40_DONGLE_NRF52840
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Raytac MDBT50Q-CX-40 Dongle board configuration
# SPDX-License-Identifier: Apache-2.0

config BOARD_MDBT50Q_CX_40_DONGLE_NRF52840
bool "Raytac MDBT50Q-CX-40 Dongle"
depends on SOC_NRF52840_QIAA
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# SPDX-License-Identifier: Apache-2.0

if BOARD_MDBT50Q_CX_40_DONGLE_NRF52840

config BOARD
default "mdbt50q_cx_40_dongle_nrf52840"

config BT_CTLR
default BT

config FLASH_LOAD_OFFSET
default 0x1000

config FLASH_LOAD_SIZE
default 0xeb000

endif # BOARD_MDBT50Q_CX_40_DONGLE_NRF52840
38 changes: 38 additions & 0 deletions firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/board.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2025 Raytac Corporation
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/init.h>
#include <nrfx_power.h>

void board_early_init_hook(void)
{
/* If powered from USB (high voltage mode), GPIO output voltage
* defaults to 1.8V which is insufficient to drive the LEDs.
* Bump REGOUT0 to 3.0V and reset so the change takes effect.
*/
if ((nrf_power_mainregstatus_get(NRF_POWER) ==
NRF_POWER_MAINREGSTATUS_HIGH) &&
((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) ==
(UICR_REGOUT0_VOUT_DEFAULT << UICR_REGOUT0_VOUT_Pos))) {

NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
__NOP();
}

NRF_UICR->REGOUT0 =
(NRF_UICR->REGOUT0 & ~((uint32_t)UICR_REGOUT0_VOUT_Msk)) |
(UICR_REGOUT0_VOUT_3V0 << UICR_REGOUT0_VOUT_Pos);

NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
__NOP();
}

/* Reset required for UICR changes to take effect */
NVIC_SystemReset();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: Apache-2.0

board_runner_args(jlink "--device=nRF52840_xxAA" "--speed=4000")
board_runner_args(pyocd "--target=nrf52840" "--frequency=4000000")
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)
include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake)
include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake)
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (c) 2022 Nordic Semiconductor
* SPDX-License-Identifier: Apache-2.0
*/

&pinctrl {
uart0_default: uart0_default {
group1 {
psels = <NRF_PSEL(UART_TX, 0, 20)>,
<NRF_PSEL(UART_RX, 0, 24)>;
};
};

uart0_sleep: uart0_sleep {
group1 {
psels = <NRF_PSEL(UART_TX, 0, 20)>,
<NRF_PSEL(UART_RX, 0, 24)>;
low-power-enable;
};
};
};

Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright (c) 2018-2023 Nordic Semiconductor ASA
* Copyright (c) 2025 Raytac Corporation
* SPDX-License-Identifier: Apache-2.0
*/

/dts-v1/;
#include <nordic/nrf52840_qiaa.dtsi>
#include "mdbt50q_cx_40_dongle_nrf52840-pinctrl.dtsi"

/ {
model = "Raytac MDBT50Q-CX-40 Dongle";
compatible = "raytac,mdbt50q-cx-40-dongle";

chosen {
zephyr,console = &uart0;
zephyr,shell-uart = &uart0;
zephyr,uart-mcumgr = &uart0;
zephyr,bt-mon-uart = &uart0;
zephyr,bt-c2h-uart = &uart0;
zephyr,sram = &sram0;
zephyr,flash = &flash0;
zephyr,code-partition = &code_partition;
};

leds {
compatible = "gpio-leds";
led0: led_0 {
gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;
label = "Green LED";
};
led1: led_1 {
gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
label = "Blue LED";
};
};

buttons {
compatible = "gpio-keys";
button0: button_0 {
gpios = <&gpio1 6 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
label = "Push button switch 0";
};
};

aliases {
led0 = &led0;
led1 = &led1;
sw0 = &button0;
};
};

&gpiote {
status = "okay";
};

&gpio0 {
status = "okay";
};

&gpio1 {
status = "okay";
};

&uart0 {
compatible = "nordic,nrf-uart";
current-speed = <115200>;
status = "okay";
pinctrl-0 = <&uart0_default>;
pinctrl-1 = <&uart0_sleep>;
pinctrl-names = "default", "sleep";
};

&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;

/* Nordic MBR occupies 0x0 - 0x1000.
* Application is loaded by Nordic nRF5 DFU bootloader
* which lives at 0xf4000 - 0xfe000 (outside these partitions).
*/
code_partition: partition@1000 {
label = "code_partition";
reg = <0x00001000 0x000eb000>;
};

storage_partition: partition@ec000 {
label = "storage";
reg = <0x000ec000 0x00008000>;
};
};
};

zephyr_udc0: &usbd {
compatible = "nordic,nrf-usbd";
status = "okay";
};

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
identifier: mdbt50q_cx_40_dongle_nrf52840
name: Raytac MDBT50Q-CX-40 Dongle
type: mcu
arch: arm
toolchain:
- zephyr
- gnuarmemb
supported:
- usb_device
- ble
- gpio
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# SPDX-License-Identifier: Apache-2.0

CONFIG_SOC_SERIES_NRF52X=y
CONFIG_SOC_NRF52840_QIAA=y
CONFIG_BOARD_MDBT50Q_CX_40_DONGLE_NRF52840=y

# Enable MPU
CONFIG_ARM_MPU=y

# Enable GPIO
CONFIG_GPIO=y

# Enable UART
CONFIG_SERIAL=y

# Board options
CONFIG_GPIO_AS_PINRESET=y
CONFIG_PINCTRL=y

# Nordic nRF5 DFU bootloader support: link app after Nordic MBR at 0x1000
# CONFIG_BOARD_HAS_NRF5_BOOTLOADER=y
# CONFIG_FLASH_LOAD_OFFSET=0x1000
# CONFIG_FLASH_LOAD_SIZE=0xeb000

# USB: disable remote wakeup (nrfx driver conflict)
CONFIG_USB_DEVICE_REMOTE_WAKEUP=n
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: Apache-2.0

list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled")