From 2e5519ebecd68fb4a3a4556bc65ef701e843b68d Mon Sep 17 00:00:00 2001 From: lh-128 <59599222+lh-128@users.noreply.github.com> Date: Mon, 23 Feb 2026 15:38:47 -0800 Subject: [PATCH] Add support for Raytac MDBT50Q-CX-40 Dongle - Device config for raytac_mdbt50q_cx_40_dongle - Update workflow file to export zip file (as the board uses DFU bootloader, not UF2) - Update documents --- .github/workflows/build-nrf52.yml | 26 ++++- BLUETOOTH.md | 28 ++++- README.md | 5 +- .../arm/mdbt50q_cx_40_dongle/CMakeLists.txt | 4 + .../boards/arm/mdbt50q_cx_40_dongle/Kconfig | 7 ++ .../arm/mdbt50q_cx_40_dongle/Kconfig.board | 6 ++ .../mdbt50q_cx_40_dongle/Kconfig.defconfig | 17 +++ .../boards/arm/mdbt50q_cx_40_dongle/board.c | 38 +++++++ .../arm/mdbt50q_cx_40_dongle/board.cmake | 7 ++ ...mdbt50q_cx_40_dongle_nrf52840-pinctrl.dtsi | 22 ++++ .../mdbt50q_cx_40_dongle_nrf52840.dts | 100 ++++++++++++++++++ .../mdbt50q_cx_40_dongle_nrf52840.yaml | 11 ++ .../mdbt50q_cx_40_dongle_nrf52840_defconfig | 26 +++++ .../mdbt50q_cx_40_dongle/pre_dt_board.cmake | 3 + 14 files changed, 294 insertions(+), 6 deletions(-) create mode 100644 firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/CMakeLists.txt create mode 100644 firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/Kconfig create mode 100644 firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/Kconfig.board create mode 100644 firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/Kconfig.defconfig create mode 100644 firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/board.c create mode 100644 firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/board.cmake create mode 100644 firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/mdbt50q_cx_40_dongle_nrf52840-pinctrl.dtsi create mode 100644 firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/mdbt50q_cx_40_dongle_nrf52840.dts create mode 100644 firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/mdbt50q_cx_40_dongle_nrf52840.yaml create mode 100644 firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/mdbt50q_cx_40_dongle_nrf52840_defconfig create mode 100644 firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/pre_dt_board.cmake diff --git a/.github/workflows/build-nrf52.yml b/.github/workflows/build-nrf52.yml index a313d59d..6dd33b08 100644 --- a/.github/workflows/build-nrf52.yml +++ b/.github/workflows/build-nrf52.yml @@ -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 }} diff --git a/BLUETOOTH.md b/BLUETOOTH.md index 5efa40f4..cf0def11 100644 --- a/BLUETOOTH.md +++ b/BLUETOOTH.md @@ -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. @@ -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 \ No newline at end of file diff --git a/README.md b/README.md index 381e9e01..98fc3e7b 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/CMakeLists.txt b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/CMakeLists.txt new file mode 100644 index 00000000..9e33852e --- /dev/null +++ b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/CMakeLists.txt @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources(board.c) diff --git a/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/Kconfig b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/Kconfig new file mode 100644 index 00000000..ec06ba4e --- /dev/null +++ b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/Kconfig @@ -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 diff --git a/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/Kconfig.board b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/Kconfig.board new file mode 100644 index 00000000..52c3ef2d --- /dev/null +++ b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/Kconfig.board @@ -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 diff --git a/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/Kconfig.defconfig b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/Kconfig.defconfig new file mode 100644 index 00000000..eff38fce --- /dev/null +++ b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/Kconfig.defconfig @@ -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 diff --git a/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/board.c b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/board.c new file mode 100644 index 00000000..543955f3 --- /dev/null +++ b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/board.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2025 Raytac Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +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(); + } +} diff --git a/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/board.cmake b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/board.cmake new file mode 100644 index 00000000..869d1043 --- /dev/null +++ b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/board.cmake @@ -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) diff --git a/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/mdbt50q_cx_40_dongle_nrf52840-pinctrl.dtsi b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/mdbt50q_cx_40_dongle_nrf52840-pinctrl.dtsi new file mode 100644 index 00000000..cf5bd187 --- /dev/null +++ b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/mdbt50q_cx_40_dongle_nrf52840-pinctrl.dtsi @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor + * SPDX-License-Identifier: Apache-2.0 + */ + +&pinctrl { + uart0_default: uart0_default { + group1 { + psels = , + ; + }; + }; + + uart0_sleep: uart0_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; +}; + diff --git a/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/mdbt50q_cx_40_dongle_nrf52840.dts b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/mdbt50q_cx_40_dongle_nrf52840.dts new file mode 100644 index 00000000..9817fdee --- /dev/null +++ b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/mdbt50q_cx_40_dongle_nrf52840.dts @@ -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 +#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"; +}; + diff --git a/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/mdbt50q_cx_40_dongle_nrf52840.yaml b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/mdbt50q_cx_40_dongle_nrf52840.yaml new file mode 100644 index 00000000..993208ce --- /dev/null +++ b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/mdbt50q_cx_40_dongle_nrf52840.yaml @@ -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 diff --git a/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/mdbt50q_cx_40_dongle_nrf52840_defconfig b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/mdbt50q_cx_40_dongle_nrf52840_defconfig new file mode 100644 index 00000000..bde96d3f --- /dev/null +++ b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/mdbt50q_cx_40_dongle_nrf52840_defconfig @@ -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 diff --git a/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/pre_dt_board.cmake b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/pre_dt_board.cmake new file mode 100644 index 00000000..09f5efe4 --- /dev/null +++ b/firmware-bluetooth/boards/arm/mdbt50q_cx_40_dongle/pre_dt_board.cmake @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 + +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled")