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
57 changes: 57 additions & 0 deletions Boards.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@
#define MODEL_11 0x11 // RAK4631, 433 Mhz
#define MODEL_12 0x12 // RAK4631, 868 Mhz

#define BOARD_XIAO_NRF52840 0x52 // Seeed XIAO nRF52840 + Wio-SX1262

#define PRODUCT_HMBRW 0xF0
#define BOARD_HMBRW 0x32
#define BOARD_HUZZAH32 0x34
Expand Down Expand Up @@ -142,6 +144,8 @@
#ifndef MODEM
#if BOARD_MODEL == BOARD_RAK4631
#define MODEM SX1262
#elif BOARD_MODEL == BOARD_XIAO_NRF52840
#define MODEM SX1262
#elif BOARD_MODEL == BOARD_GENERIC_NRF52
#define MODEM SX1262
#else
Expand Down Expand Up @@ -898,6 +902,59 @@
const int DISPLAY_BL_PIN = PIN_T114_TFT_BLGT;
const int DISPLAY_RST = PIN_T114_TFT_RST;

#elif BOARD_MODEL == BOARD_XIAO_NRF52840
#define HAS_EEPROM false
#define HAS_DISPLAY false
#define HAS_BLUETOOTH false
#undef HAS_BLE
#define HAS_BLE true
// Seeed platform bootloader does not write image size at the Adafruit
// nRF52 IMG_SIZE_START address, so firmware hash validation is disabled.
#undef VALIDATE_FIRMWARE
#define VALIDATE_FIRMWARE false
#define HAS_CONSOLE false
#define HAS_PMU false
#define HAS_NP false
#define HAS_SD false
#undef HAS_TCXO
#define HAS_TCXO true
#define HAS_RF_SWITCH_RX_TX true
#undef HAS_BUSY
#define HAS_BUSY true
#define DIO2_AS_RF_SWITCH true
#define CONFIG_UART_BUFFER_SIZE 6144
#define CONFIG_QUEUE_SIZE 6144
#define CONFIG_QUEUE_MAX_LENGTH 200
#define EEPROM_SIZE 296
#define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
#define BLE_MANUFACTURER "Seeed Studio"
#define BLE_MODEL "XIAO nRF52840"

// XIAO nRF52840 onboard LEDs are active-low
// (LOW turns LED on, HIGH turns LED off)
#define LED_ON LOW
#define LED_OFF HIGH

#undef HAS_SLEEP
#define HAS_SLEEP true
#define PIN_WAKEUP 1 // DIO1 from radio (Arduino D1)
#define WAKEUP_LEVEL HIGH // DIO1 goes high on interrupt

// Pin mapping for XIAO nRF52840 + Wio-SX1262 kit
// Using Arduino digital pin numbers (D0-D10), not raw GPIO
// The Arduino framework maps these via g_ADigitalPinMap
const int pin_rxen = 5; // D5 → P0.05 (RF_SW)
const int pin_reset = 2; // D2 → P0.28 (RST)
const int pin_cs = 4; // D4 → P0.04 (NSS)
const int pin_sclk = 8; // D8 → P1.13 (SCK)
const int pin_mosi = 10; // D10 → P1.15 (MOSI)
const int pin_miso = 9; // D9 → P1.14 (MISO)
const int pin_busy = 3; // D3 → P0.29 (BUSY)
const int pin_dio = 1; // D1 → P0.03 (DIO1/IRQ)
const int pin_led_rx = LED_RED;
const int pin_led_tx = LED_RED;
const int pin_tcxo_enable = -1;

#else
#error An unsupported nRF board was selected. Cannot compile RNode firmware.
#endif
Expand Down
70 changes: 68 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,69 @@ When in `Transport Mode`, the device will display "TRANSPORT" across the top of

Note that at the present time, when in TNC mode this firmware does not operate like a regular RNode does when in TNC mode due to logging from the embedded RNS that is output on the serial port. This can clobber KISS communication from the attached machine so do not attempt to attach another RNS to the device while in this mode. On the plus side, there is extensive logging available on the serial port to observe the embedded RNS in action and to aid in troubleshooting.

## Supported Hardware

### Seeed XIAO nRF52840 + Wio-SX1262

The [Seeed XIAO nRF52840](https://wiki.seeedstudio.com/XIAO_BLE/) paired with the [Wio-SX1262 expansion board](https://wiki.seeedstudio.com/wio_sx1262_with_xiao_nrf52840_kit/) is a compact, low-power LoRa node well suited for deployment as a standalone Reticulum transport/repeater.

**Pin mapping (do not modify — consumed by radio):**

| Function | Arduino pin | nRF52840 GPIO |
|----------|-------------|---------------|
| RF switch (RXEN) | D5 | P0.05 |
| Radio reset | D2 | P0.28 |
| SPI CS | D4 | P0.04 |
| SPI SCK | D8 | P1.13 |
| SPI MOSI | D10 | P1.15 |
| SPI MISO | D9 | P1.14 |
| BUSY | D3 | P0.29 |
| DIO1 / IRQ | D1 | P0.03 |

Free GPIO for external use: D0, D6, D7.

**PlatformIO environments:**

| Environment | Description |
|-------------|-------------|
| `xiao_nrf52840` | Standard build — continuous RX, ~10 mA |

**Build and flash:**

The Seeed platform is downloaded automatically on the first build.
```
pio run -e xiao_nrf52840
```

Flashing uses the nRF52 UF2 DFU bootloader. Double-tap the reset button on the XIAO to enter bootloader mode (the LED pulses slowly), then upload:
```
pio run -e xiao_nrf52840 -t upload
```
The post-upload script automatically provisions the EEPROM and writes the firmware hash via `rnodeconf`.

**Configuration:**

For standalone TNC / transport mode:
```
rnodeconf /dev/ttyACM0 --tnc --freq 869525000 --bw 125000 --sf 8 --cr 5 --txp 14
```

For host-controlled mode (attached RNS / meshchat):
```
rnodeconf /dev/ttyACM0 --normal
```

Verify with:
```
rnodeconf /dev/ttyACM0 --info
```

The onboard red LED indicates radio state: briefly lit on RX/TX activity in HOST mode; lit while the channel is sensed busy (CSMA) in TNC mode.

**Notes:**
- The Seeed bootloader does not write the image size at the Adafruit nRF52 `IMG_SIZE_START` address, so `VALIDATE_FIRMWARE` is disabled for this board to prevent the firmware hash check from failing.
- No dedicated `rnodeconf` product/model ID exists for the XIAO nRF52840 yet; the RAK4631 bytes (`0x10`/`0x12`) are reused during provisioning.

## Build Dependencies

Build environment is configured for use in [VSCode](https://code.visualstudio.com/) and [PlatformIO](https://platformio.org/).
Expand All @@ -37,7 +100,7 @@ Build environment is configured for use in [VSCode](https://code.visualstudio.co
Building and uploading to hardware is simple through the VSCode PlatformIO IDE
- Install VSCode and PlatformIO
- Clone this repo
- Lanch PlatformIO and load repo
- Launch PlatformIO and load repo
- In PlatformIO, select the environment for intended board
- Build, Upload, and Monitor to observe application logging

Expand Down Expand Up @@ -66,18 +129,21 @@ Build a single environment (board):
```
pio run -e ttgo-t-beam
pio run -e wiscore_rak4631
pio run -e xiao_nrf52840
```

Build and upload a single environment (board):
```
pio run -e ttgo-t-beam -t upload
pio run -e wiscore_rak4631 -t upload
pio run -e xiao_nrf52840 -t upload
```

Build and package a single environment (board):
```
pio run -e ttgo-t-beam -t package
pio run -e wiscore_rak4631 -t package
pio run -e xiao_nrf52840 -t package
```

Build all environments (boards):
Expand Down Expand Up @@ -122,8 +188,8 @@ New firmware release procedure:

- [ ] Extend KISS interface to support config/control of the integrated microReticulum stack
- [ ] Add interface for easy customization of firmware
- [ ] Add power management and sleep states to extend battery runtime
- [x] Add build targets for NRF52 boards
- [x] Add build target for Seeed XIAO nRF52840 + Wio-SX1262

Please open an Issue if you have trouble building ior using the API, and feel free to start a new Discussion for anything else.

24 changes: 19 additions & 5 deletions RNode_Firmware.ino
Original file line number Diff line number Diff line change
Expand Up @@ -378,11 +378,11 @@ void setup() {
boot_seq();
#endif

#if BOARD_MODEL != BOARD_RAK4631 && BOARD_MODEL != BOARD_HELTEC_T114 && BOARD_MODEL != BOARD_TECHO && BOARD_MODEL != BOARD_T3S3 && BOARD_MODEL != BOARD_TBEAM_S_V1 && BOARD_MODEL != BOARD_HELTEC32_V4
#if BOARD_MODEL != BOARD_RAK4631 && BOARD_MODEL != BOARD_HELTEC_T114 && BOARD_MODEL != BOARD_TECHO && BOARD_MODEL != BOARD_T3S3 && BOARD_MODEL != BOARD_TBEAM_S_V1 && BOARD_MODEL != BOARD_HELTEC32_V4 && BOARD_MODEL != BOARD_XIAO_NRF52840
// Some boards need to wait until the hardware UART is set up before booting
// the full firmware. In the case of the RAK4631 and Heltec T114, the line below will wait
// until a serial connection is actually established with a master. Thus, it
// is disabled on this platform.
// the full firmware. In the case of the RAK4631, XIAO nRF52840, and Heltec T114,
// the line below will wait until a serial connection is actually established
// with a master. Thus, it is disabled on these platforms.
while (!Serial);
#endif

Expand Down Expand Up @@ -1004,6 +1004,7 @@ bool startRadio() {

LoRa->enableCrc();
LoRa->onReceive(receive_callback);

lora_receive();

// Flash an info pattern to indicate
Expand Down Expand Up @@ -1822,6 +1823,7 @@ void serial_callback(uint8_t sbyte) {

bool medium_free() {
update_modem_status();
update_noise_floor();
if (avoid_interference && interference_detected) { return false; }
return !dcd;
}
Expand Down Expand Up @@ -2295,10 +2297,22 @@ void sleep_now() {
delay(2000);
analogWrite(PIN_VEXT_EN, 0);
delay(100);
#endif
#elif BOARD_MODEL == BOARD_XIAO_NRF52840
// XIAO nRF52840: put radio to sleep, configure DIO1 as wakeup, enter System OFF
if (radio_online) {
LoRa->sleep();
}
#if defined(PIN_WAKEUP)
nrf_gpio_cfg_sense_input(g_ADigitalPinMap[PIN_WAKEUP],
NRF_GPIO_PIN_PULLDOWN,
NRF_GPIO_PIN_SENSE_HIGH);
#endif
NRF_POWER->SYSTEMOFF = 1;
#else
sd_power_gpregret_set(0, 0x6d);
nrf_gpio_cfg_sense_input(pin_btn_usr1, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
NRF_POWER->SYSTEMOFF = 1;
#endif
#endif
#endif
}
Expand Down
14 changes: 12 additions & 2 deletions Utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
using namespace Adafruit_LittleFS_Namespace;
#define EEPROM_FILE "eeprom"
bool file_exists = false;
int written_bytes = 4;
int written_bytes = 0;
File file(InternalFS);
#endif
#include <stddef.h>
Expand Down Expand Up @@ -376,6 +376,14 @@ extern RNS::Reticulum reticulum;
void led_tx_off() { digitalWrite(pin_led_tx, LED_OFF); }
void led_id_on() { }
void led_id_off() { }
#elif BOARD_MODEL == BOARD_XIAO_NRF52840
// XIAO LEDs are active-low (LED_ON=LOW, LED_OFF=HIGH); rx and tx share LED_RED
void led_rx_on() { digitalWrite(pin_led_rx, LED_ON); }
void led_rx_off() { digitalWrite(pin_led_rx, LED_OFF); }
void led_tx_on() { digitalWrite(pin_led_tx, LED_ON); }
void led_tx_off() { digitalWrite(pin_led_tx, LED_OFF); }
void led_id_on() { }
void led_id_off() { }
#endif
#endif

Expand Down Expand Up @@ -1714,6 +1722,8 @@ bool eeprom_model_valid() {
#elif BOARD_MODEL == BOARD_HELTEC_T114
if (model == MODEL_C6 || model == MODEL_C7) {
#elif BOARD_MODEL == BOARD_RAK4631
if (model == MODEL_11 || model == MODEL_12) {
#elif BOARD_MODEL == BOARD_XIAO_NRF52840
if (model == MODEL_11 || model == MODEL_12) {
#elif BOARD_MODEL == BOARD_HUZZAH32
if (model == MODEL_FF) {
Expand Down Expand Up @@ -1868,7 +1878,7 @@ void eeprom_conf_load() {
}

void eeprom_conf_save() {
if (hw_ready && radio_online) {
if (hw_ready) {
eeprom_update(eeprom_addr(ADDR_CONF_SF), lora_sf);
eeprom_update(eeprom_addr(ADDR_CONF_CR), lora_cr);
eeprom_update(eeprom_addr(ADDR_CONF_TXP), lora_txp);
Expand Down
17 changes: 11 additions & 6 deletions extra_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ def pre_upload(source, target, env):
print("*** Executing pre_upload steps...")
# do some actions

def is_nrf52_platform(platform):
return "nordicnrf52" in platform or "seeedboards" in platform or "platform-seeedboards" in platform

def post_upload(source, target, env):
print("*** Executing post_upload steps...")
print("Platform:", env.GetProjectOption("platform"))
Expand All @@ -43,9 +46,8 @@ def post_upload(source, target, env):
firmware_hash(source, env)
# firmware pacakaging is incomplete due to missing console image
#firmware_package(env)
elif ("nordicnrf52" in platform):
elif is_nrf52_platform(platform):
time.sleep(5)
# device provisioning is incomplete and only currently appropriate for 915MHz RAK4631
device_provision(env)
time.sleep(5)
firmware_hash(source, env)
Expand Down Expand Up @@ -95,6 +97,10 @@ def device_provision(env):
env.Execute("rnodeconf --product 10 --model 12 --hwrev 1 --rom " + env.subst("$UPLOAD_PORT"))
case "heltec_t114" | "heltec_t114_local":
env.Execute("rnodeconf --product c2 --model c7 --hwrev 1 --rom " + env.subst("$UPLOAD_PORT"))
case "xiao_nrf52840":
# No dedicated rnodeconf product ID for XIAO nRF52840 yet; reuse RAK4631
# product/model bytes (0x10/0x12) — same MCU+SX1262 stack, 868/915 MHz band
env.Execute("rnodeconf --product 10 --model 12 --hwrev 1 --rom " + env.subst("$UPLOAD_PORT"))
case _:
print(f"Unknown board variant {variant}, can not provision device!")

Expand All @@ -104,10 +110,9 @@ def firmware_hash(source, env):
source_file = source[0].get_abspath()
platform = env.GetProjectOption("platform")
print("Platform:", platform)
if (platform == "nordicnrf52"):
if is_nrf52_platform(platform):
build_dir = env.subst("$BUILD_DIR")
env.Execute("cd " + build_dir + "; unzip -o " + source_file + " " + env.subst("$PROGNAME") + ".bin")
#source_file.replace(".zip", ".bin")
source_file = build_dir + "/" + env.subst("$PROGNAME") + ".bin";
print("source_file:", source_file)
firmware_data = open(source_file, "rb").read()
Expand Down Expand Up @@ -171,7 +176,7 @@ def firmware_package(env):
zip_cmd += build_dir + "/" + env.subst("$PROGNAME") + ".elf "
zip_cmd += build_dir + "/" + env.subst("$PROGNAME") + ".map "
env.Execute(zip_cmd)
elif (platform == "nordicnrf52"):
elif is_nrf52_platform(platform):
env.Execute("cp " + build_dir + "/" + env.subst("$PROGNAME") + ".zip " + project_dir + "/Release/.")
env.Execute("python " + project_dir + "/release_hashes.py > " + project_dir + "/Release/release.json")

Expand Down Expand Up @@ -207,7 +212,7 @@ def firmware_package(env):
title="Package",
description="Package esp32 firmware for delivery"
)
elif (platform == "nordicnrf52"):
elif is_nrf52_platform(platform):
# remove --specs=nano.specs to allow exceptions to work
if '--specs=nano.specs' in env['LINKFLAGS']:
env['LINKFLAGS'].remove('--specs=nano.specs')
Expand Down
28 changes: 28 additions & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -991,3 +991,31 @@ lib_deps =
adafruit/Adafruit NeoPixel@^1.12.0
microStore=symlink://../microStore
microReticulum=symlink://../microReticulum

; MCU: Nordic nRF52840
; SRAM: 256 KB
; Flash: 1 MB internal
; LoRa: Semtech SX1262 (via Wio-SX1262 expansion board)
; Power: Battery/solar optimized with duty-cycle RX support
[env:xiao_nrf52840]
platform = https://github.com/Seeed-Studio/platform-seeedboards.git
board = seeed-xiao-afruitnrf52-nrf52840
framework = arduino
custom_variant = xiao_nrf52840
lib_archive = no
build_flags =
${env.build_flags}
-fexceptions
-DBOARD_MODEL=BOARD_XIAO_NRF52840
-DUSTORE_USE_INTERNALFS
-DURTN_PATH_TABLE_MAX_RECS=25
-DRNS_USE_ALLOCATOR=1
-DRNS_USE_TLSF=1
-Wl,--allow-multiple-definition
build_unflags = -fno-exceptions
lib_deps =
${env.lib_deps}
XPowersLib@^0.2.1
https://github.com/attermann/microStore.git
https://github.com/attermann/microReticulum.git

Loading