rnode compatible firmware for ch32v003 and E22 900m22s ebyte semtech module
Find a file
2026-04-10 07:55:36 +02:00
core fixed fragment transmission bug 2026-04-07 07:20:27 +02:00
drivers/ch32v003 fixed fragment transmission bug 2026-04-07 07:20:27 +02:00
handlers review and correct comments in code, cause a code without comment is a dead code 2026-04-04 11:07:10 +02:00
link Initial import of os3-rnode 2026-03-29 18:44:59 +02:00
subfsm fix issue 2026-04-10 07:55:36 +02:00
.gitignore update .gitignore 2026-04-07 07:21:40 +02:00
README.md Clarify Wio SX1262 wiring is unverified 2026-03-31 06:28:50 +02:00
run.sh Initial import of os3-rnode 2026-03-29 18:44:59 +02:00

os3-rnode

Minimal standalone CH32V003 RNode-compatible LoRa modem firmware extracted from the OS3 tree.

Article and project write-up:

This repository contains only the files required to build the CH32V003 + SX1262 RNode firmware image:

  • core event kernel: core/trap.S, core/event.S, core/kernel.S, core/timer.S, core/console.S, core/fsm.S, core/events.inc
  • CH32V003 drivers: drivers/ch32v003/bootstrap.S, init.S, exti.S, uart.S, vector.S, flash.S, mcu.S, spi.S, sx1262.S, led.S, mmio.inc
  • FSMs: subfsm/rnode_fsm.S, subfsm/lora_fsm.S
  • handlers: handlers/heartbeat.S
  • linker: link/ch32v003.ld
  • build entrypoint: run.sh

The extraction source commit in the original OS3 tree was: 3058b75912041138a990446e3c946457b5da8ef7

Build

The repository builds a single firmware target: CH32V003.

There is no Makefile or external build system here. The whole build is driven by run.sh, which:

  • creates build/ch32v003/
  • removes previous .o, .elf, and .bin files for that target
  • assembles every module with riscv32-unknown-elf-as
  • links the final image with riscv32-unknown-elf-ld
  • generates the flashable binary with riscv32-unknown-elf-objcopy
  • prints the resulting image sizes and registered .kernel_init entries

Prerequisites

Install a RISC-V embedded GNU toolchain and make sure these commands are available in your PATH:

  • riscv32-unknown-elf-as
  • riscv32-unknown-elf-ld
  • riscv32-unknown-elf-objcopy
  • riscv32-unknown-elf-size
  • riscv32-unknown-elf-nm

The firmware is assembled for rv32ec_zicsr with ABI ilp32e, matching the CH32V003.

Build Command

From the repository root, run:

./run.sh

On a successful build, the script produces:

  • build/ch32v003/kernel.elf - ELF image with symbols and debug info
  • build/ch32v003/kernel.bin - raw binary image to flash to the MCU

The script is safe to re-run; it cleans the target output directory before rebuilding.

Typical Build Output

The build ends with a short summary similar to:

Build complete:
- build/ch32v003/kernel.elf
- build/ch32v003/kernel.bin

Section sizes:
   text   data    bss    dec    hex filename
   ....

If one of the riscv32-unknown-elf-* tools is missing, the build will fail immediately. In that case, fix the toolchain installation or PATH first.

Flashing

The build output to flash is:

  • build/ch32v003/kernel.bin

The simplest workflow uses minichlink with a WCH-Link compatible probe.

Write the firmware to flash:

minichlink -w build/ch32v003/kernel.bin flash -b

Write and reset immediately after flashing:

minichlink -w build/ch32v003/kernel.bin flash -b -r

Typical sequence:

  1. Connect the CH32V003 board to the SX1262 radio module.
  2. Connect the SWD probe to the CH32V003.
  3. Build the firmware with ./run.sh.
  4. Flash build/ch32v003/kernel.bin with minichlink.
  5. Reset the board and reconnect the host UART side.

If your probe is already attached and detected, the second command is usually the one you want:

minichlink -w build/ch32v003/kernel.bin flash -b -r

Wiring

The current firmware is known to work with SX1262 modules that use:

  • DIO1 as the IRQ line
  • BUSY, NSS, SCK, MOSI, MISO, and NRST directly
  • DIO3 internally for the TCXO
  • one external RX-side RF switch control line driven by PC3
  • SX1262 DIO2 RF-switch control for the TX-side path

That wiring model is confirmed for the E22 module documented below. Other SX1262 modules that expose equivalent signals may also work, but should be treated as unverified until tested on hardware.

CH32V003 to E22-900M22S

Connect the radio module to the CH32V003 exactly like this:

  • PC0 -> NRST
  • PC1 -> BUSY
  • PC2 -> DIO1
  • PC3 -> RXEN
  • PC4 -> NSS / CS
  • PC5 -> SCK
  • PC6 -> MOSI
  • PC7 -> MISO
  • 3V3 -> VCC
  • GND -> GND
  • DIO2 -> TXEN on the E22 side

Attach an antenna before RF testing.

CH32V003 to Seeed Wio-SX1262 / XIAO SX1262 carrier

For the Seeed Wio-SX1262 module and XIAO carrier variants that expose the module RF_SW pin, the following wiring might work, but it has not been validated in this repository:

  • PC0 -> NRST
  • PC1 -> BUSY
  • PC2 -> DIO1
  • PC3 -> RF_SW
  • PC4 -> NSS / CS
  • PC5 -> SCK
  • PC6 -> MOSI
  • PC7 -> MISO
  • 3V3 -> VCC
  • GND -> GND

Do not add an external DIO2 wire for this module. The Wio-SX1262 datasheet states that DIO2 is internally connected to the RF switch for transmitter mode, while RF_SW is the external control input for receiver mode.

The reason this might work is that it appears to match the current firmware logic:

  • PC3 is driven low in standby/TX
  • PC3 is driven high in RX
  • SX1262 DIO2 RF-switch control is enabled during radio init

This has not been validated on hardware in this repository yet, so do not treat it as confirmed support. It is only a documented compatibility hypothesis until someone completes TX and RX smoke tests on the target board.

For the host-side RNode UART, connect the CH32V003 USART1 pins to the WCH-Link UART bridge:

  • PD5 (USART1 TX) -> WCH-Link UART RX
  • PD6 (USART1 RX) -> WCH-Link UART TX
  • GND -> GND

For flashing and debug access, connect the WCH-Link probe like this:

  • PD1 (SWDIO / SWIO) -> WCH-Link SWDIO
  • PD0 (NRST) -> WCH-Link NRST if available
  • 3V3 -> WCH-Link 3V3
  • GND -> GND

CH32V003 uses a single-wire debug interface on PD1, so there is no separate SWCLK pin to wire.

LEDs

The firmware uses two status LEDs on port D:

  • PD4 -> heartbeat LED
  • PD2 -> radio activity LED

The radio activity LED is active-low in the current driver.