From d2cf4b644952e3adc842288709fea9839913208e Mon Sep 17 00:00:00 2001 From: Sergey Maranchuk Date: Tue, 9 Dec 2025 16:22:39 +0200 Subject: [PATCH] Support for XIAO nRF52840 & Wio-SX1262 Kit --- Boards.h | 62 +++++++++++++++++++++++++++++++++++++++++++ Makefile | 14 ++++++++++ Power.h | 22 +++++++++++++-- RNode_Firmware_CE.ino | 25 ++++++++++++----- Radio.cpp | 2 ++ Utilities.h | 9 ++++--- 6 files changed, 122 insertions(+), 12 deletions(-) diff --git a/Boards.h b/Boards.h index 3a41183..f7f399d 100644 --- a/Boards.h +++ b/Boards.h @@ -123,6 +123,10 @@ #define MODEL_16 0x16 // T-Echo 433 MHz #define MODEL_17 0x17 // T-Echo 868/915 MHz + #define PRODUCT_XIAO_NRF 0x18 // Seeed XIAO nRF52840 devices + #define BOARD_XIAO_NRF 0x53 + #define MODEL_19 0x19 // XIAO nRF52840 with Wio-SX1262, 868 MHz + #define PRODUCT_HMBRW 0xF0 #define BOARD_HMBRW 0x32 #define BOARD_HUZZAH32 0x34 @@ -1390,6 +1394,64 @@ #define PIN_GPS_RX 37 #define PIN_GPS_TX 39 #endif + + #elif BOARD_MODEL == BOARD_XIAO_NRF + #define HAS_EEPROM false + #define HAS_DISPLAY false + #define HAS_BLUETOOTH false + #define HAS_BLE true + #define HAS_CONSOLE false + #define VALIDATE_FIRMWARE true + #define HAS_PMU true + #define HAS_NP false + #define HAS_SD false + #define CONFIG_UART_BUFFER_SIZE 6144 + #define CONFIG_QUEUE_0_SIZE 6144 + #define HAS_INPUT true + #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" + + #define INTERFACE_COUNT 1 + + // Wio-SX1262 for XIAO + const uint8_t interfaces[INTERFACE_COUNT] = {SX1262}; + const bool interface_cfg[INTERFACE_COUNT][3] = { + // SX1262 + { + false, // DEFAULT_SPI + true, // HAS_TCXO + true // DIO2_AS_RF_SWITCH + } + }; + const int8_t interface_pins[INTERFACE_COUNT][10] = { + // SX1262 + { + 4, // pin_ss (CS = D4) + 8, // pin_sclk (SCK = D8) + 10, // pin_mosi (MOSI = D10) + 9, // pin_miso (MISO = D9) + 3, // pin_busy (BUSY = D3) + 1, // pin_dio (DIO1 = D1) + 2, // pin_reset (RESET = D2) + -1, // pin_txen + 5, // pin_rxen (RXEN = D5) + -1 // pin_tcxo_enable + } + }; + + const int pin_led_rx = 12; // LED_BLUE (from variant.h for non-Sense version) + const int pin_led_tx = 11; // LED_RED + const int pin_led_green = 13; // LED_GREEN + + const int pin_btn_usr1 = 0; + const uint8_t pin_vbat = 31; + + #define LED_ON LOW + #define LED_OFF HIGH + #else #error An unsupported nRF board was selected. Cannot compile RNode firmware. #endif diff --git a/Makefile b/Makefile index 3a86441..10d409b 100644 --- a/Makefile +++ b/Makefile @@ -86,6 +86,9 @@ firmware-tbeam_sx1262: check_bt_buffers firmware-techo: arduino-cli compile --fqbn adafruit:nrf52:pca10056 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x44\"" +firmware-xiao_nrf: + arduino-cli compile --fqbn Seeeduino:nrf52:xiaonRF52840 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x53\" \"-DBOARD_VARIANT=0x19\"" + firmware-t3s3: arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DBOARD_VARIANT=0xAB\"" @@ -278,6 +281,11 @@ upload-techo: @sleep 6 rnodeconf /dev/ttyACM0 --firmware-hash $$(./partition_hashes from_device /dev/ttyACM0) +upload-xiao_nrf: + arduino-cli upload -p $(or $(port), /dev/ttyACM0) --fqbn Seeeduino:nrf52:xiaonRF52840 + @sleep 6 + rnodeconf $(or $(port), /dev/ttyACM0) --firmware-hash $$(./partition_hashes from_device $(or $(port), /dev/ttyACM0)) + release: console-site spiffs-image $(shell grep ^release- Makefile | cut -d: -f1) release-hashes: @@ -431,6 +439,12 @@ release-techo: adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application build/rnode_firmware_techo.hex Release/rnode_firmware_techo.zip rm -r build +release-xiao_nrf: + arduino-cli compile --fqbn Seeeduino:nrf52:xiaonRF52840 $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x53\" \"-DBOARD_VARIANT=0x19\"" + cp build/Seeeduino.nrf52.xiaonRF52840/RNode_Firmware_CE.ino.hex build/rnode_firmware_xiao_nrf.hex + adafruit-nrfutil dfu genpkg --dev-type 0x0052 --application build/rnode_firmware_xiao_nrf.hex Release/rnode_firmware_xiao_nrf.zip + rm -r build + release-t3s3: arduino-cli compile --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" $(COMMON_BUILD_FLAGS) --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x42\" \"-DBOARD_VARIANT=0xA1\"" cp ~/.arduino15/packages/esp32/hardware/esp32/$(ARDUINO_ESP_CORE_VER)/tools/partitions/boot_app0.bin build/rnode_firmware_t3s3_sx126x.boot_app0 diff --git a/Power.h b/Power.h index 8856316..47cea56 100644 --- a/Power.h +++ b/Power.h @@ -82,6 +82,24 @@ int bat_charged_samples = 0; bool bat_voltage_dropping = false; float bat_delay_v = 0; +#elif BOARD_MODEL == BOARD_XIAO_NRF + #include "nrfx_power.h" + #define BAT_V_MIN 3.15 + #define BAT_V_MAX 4.2 + #define BAT_V_FLOAT 4.22 + #define BAT_SAMPLES 7 + #define VBAT_MV_PER_LSB (0.73242188F) // 3.0V ADC range and 12 - bit ADC resolution = 3000mV / 4096 + #define VBAT_DIVIDER_COMP (3.0) // Compensation factor for the VBAT divider + #define VBAT_MV_PER_LSB_FIN (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB) + #define PIN_VBAT pin_vbat + float bat_p_samples[BAT_SAMPLES]; + float bat_v_samples[BAT_SAMPLES]; + uint8_t bat_samples_count = 0; + int bat_discharging_samples = 0; + int bat_charging_samples = 0; + int bat_charged_samples = 0; + bool bat_voltage_dropping = false; + float bat_delay_v = 0; #elif BOARD_MODEL == BOARD_T3S3 #define BAT_V_MIN 3.15 #define BAT_V_MAX 4.217 @@ -565,8 +583,8 @@ bool init_pmu() { // Set the time of pressing the button to turn off PMU->setPowerKeyPressOffTime(XPOWERS_POWEROFF_4S); - return true; - #elif BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL + return true; + #elif BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_OPENCOM_XL || BOARD_MODEL == BOARD_XIAO_NRF // board doesn't have PMU but we can measure batt voltage // prep ADC for reading battery level diff --git a/RNode_Firmware_CE.ino b/RNode_Firmware_CE.ino index d02da4c..67e8073 100644 --- a/RNode_Firmware_CE.ino +++ b/RNode_Firmware_CE.ino @@ -52,6 +52,17 @@ interface_pins[0][2] ) }; + #elif BOARD_MODEL == BOARD_XIAO_NRF + #define INTERFACE_SPI + SPIClass interface_spi[1] = { + // SX1262 + SPIClass( + NRF_SPIM1, + interface_pins[0][3], + interface_pins[0][1], + interface_pins[0][2] + ) + }; #endif #endif @@ -179,13 +190,13 @@ 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_OPENCOM_XL - // Some boards need to wait until the hardware UART is set up before booting - // the full firmware. In the case of the RAK4631/TECHO, the line below will wait - // until a serial connection is actually established with a master. Thus, it - // is disabled on this platform. - while (!Serial); - #endif + #if BOARD_MODEL != BOARD_RAK4631 && BOARD_MODEL != BOARD_HELTEC_T114 && BOARD_MODEL != BOARD_TECHO && BOARD_MODEL != BOARD_XIAO_NRF && BOARD_MODEL != BOARD_T3S3 && BOARD_MODEL != BOARD_TBEAM_S_V1 && BOARD_MODEL != BOARD_OPENCOM_XL + // Some boards need to wait until the hardware UART is set up before booting + // the full firmware. In the case of the RAK4631/TECHO/XIAO_NRF, the line below will wait + // until a serial connection is actually established with a master. Thus, it + // is disabled on this platform. + while (!Serial); + #endif // Configure input and output pins #if HAS_INPUT diff --git a/Radio.cpp b/Radio.cpp index 52364dd..0999489 100644 --- a/Radio.cpp +++ b/Radio.cpp @@ -713,6 +713,8 @@ void sx126x::enableTCXO() { uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; #elif BOARD_MODEL == BOARD_E22_ESP32 uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; + #elif BOARD_MODEL == BOARD_XIAO_NRF + uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; #else uint8_t buf[4] = {0}; #endif diff --git a/Utilities.h b/Utilities.h index 3c0f06e..4109568 100644 --- a/Utilities.h +++ b/Utilities.h @@ -354,7 +354,7 @@ uint8_t boot_vector = 0x00; void led_tx_off() { digitalWrite(pin_led_tx, HIGH); } void led_id_on() { } void led_id_off() { } - #elif BOARD_MODEL == BOARD_TECHO + #elif BOARD_MODEL == BOARD_TECHO || BOARD_MODEL == BOARD_XIAO_NRF 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); } @@ -496,7 +496,7 @@ void led_indicate_warning(int cycles) { } led_rx_off(); } - #elif BOARD_MODEL == BOARD_TECHO + #elif BOARD_MODEL == BOARD_TECHO || BOARD_MODEL == BOARD_XIAO_NRF void led_indicate_info(int cycles) { bool forever = (cycles == 0) ? true : false; cycles = forever ? 1 : cycles; @@ -1250,6 +1250,7 @@ void setTXPower(RadioInterface* radio, int txp) { if (model == MODEL_16) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN); if (model == MODEL_17) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN); + if (model == MODEL_19) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN); if (model == MODEL_A1) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN); if (model == MODEL_A2) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN); @@ -1517,7 +1518,7 @@ bool eeprom_product_valid() { #if PLATFORM == PLATFORM_ESP32 if (rval == PRODUCT_RNODE || rval == BOARD_RNODE_NG_20 || rval == BOARD_RNODE_NG_21 || rval == PRODUCT_HMBRW || rval == PRODUCT_TBEAM || rval == PRODUCT_T32_10 || rval == PRODUCT_T32_20 || rval == PRODUCT_T32_21 || rval == PRODUCT_H32_V2 || rval == PRODUCT_H32_V3 || rval == PRODUCT_TDECK_V1 || rval == PRODUCT_TBEAM_S_V1 || rval == PRODUCT_H_W_PAPER || rval == PRODUCT_XIAO_S3) { #elif PLATFORM == PLATFORM_NRF52 - if (rval == PRODUCT_RAK4631 || rval == PRODUCT_HELTEC_T114 || rval == PRODUCT_OPENCOM_XL || rval == PRODUCT_TECHO || rval == PRODUCT_HMBRW) { + if (rval == PRODUCT_RAK4631 || rval == PRODUCT_HELTEC_T114 || rval == PRODUCT_OPENCOM_XL || rval == PRODUCT_TECHO || rval == PRODUCT_XIAO_NRF || rval == PRODUCT_HMBRW) { #else if (false) { #endif @@ -1549,6 +1550,8 @@ bool eeprom_model_valid() { if (model == MODEL_D4 || model == MODEL_D9) { #elif BOARD_MODEL == BOARD_TECHO if (model == MODEL_16 || model == MODEL_17) { + #elif BOARD_MODEL == BOARD_XIAO_NRF + if (model == MODEL_19) { #elif BOARD_MODEL == BOARD_TBEAM_S_V1 if (model == MODEL_DB || model == MODEL_DC) { #elif BOARD_MODEL == BOARD_XIAO_S3