From 0f08a3b8e06d4fa108a187189fc9d1d4ef21b36a Mon Sep 17 00:00:00 2001 From: John Gagliardi Date: Thu, 19 Feb 2026 16:43:39 -0800 Subject: [PATCH 1/2] Add Muzi Base Uno and Base Duo board support New variant for the Muzi Works Base Uno (nRFLR1262, SX1262) and Base Duo (nRFLR1121, LR1121) boards. Both share the same PCB with QWIIC I2C, QSPI flash, USB-C, and battery management. The Duo reuses the Uno board class and pin definitions, differing only in the radio driver (CustomLR1110 vs CustomSX1262). Build environments per board: - repeater (headless), repeater_oled (with SSD1306 display) - companion_radio_usb, companion_radio_ble - room_server --- boards/muzi_base_duo.json | 72 ++++++++ boards/muzi_base_uno.json | 72 ++++++++ variants/muzi_base_duo/platformio.ini | 123 ++++++++++++++ variants/muzi_base_duo/target.cpp | 82 +++++++++ variants/muzi_base_duo/target.h | 27 +++ variants/muzi_base_uno/MuziBaseUnoBoard.cpp | 48 ++++++ variants/muzi_base_uno/MuziBaseUnoBoard.h | 37 ++++ variants/muzi_base_uno/platformio.ini | 120 +++++++++++++ variants/muzi_base_uno/target.cpp | 54 ++++++ variants/muzi_base_uno/target.h | 27 +++ variants/muzi_base_uno/variant.cpp | 46 +++++ variants/muzi_base_uno/variant.h | 176 ++++++++++++++++++++ 12 files changed, 884 insertions(+) create mode 100644 boards/muzi_base_duo.json create mode 100644 boards/muzi_base_uno.json create mode 100644 variants/muzi_base_duo/platformio.ini create mode 100644 variants/muzi_base_duo/target.cpp create mode 100644 variants/muzi_base_duo/target.h create mode 100644 variants/muzi_base_uno/MuziBaseUnoBoard.cpp create mode 100644 variants/muzi_base_uno/MuziBaseUnoBoard.h create mode 100644 variants/muzi_base_uno/platformio.ini create mode 100644 variants/muzi_base_uno/target.cpp create mode 100644 variants/muzi_base_uno/target.h create mode 100644 variants/muzi_base_uno/variant.cpp create mode 100644 variants/muzi_base_uno/variant.h diff --git a/boards/muzi_base_duo.json b/boards/muzi_base_duo.json new file mode 100644 index 000000000..1e0760ec2 --- /dev/null +++ b/boards/muzi_base_duo.json @@ -0,0 +1,72 @@ +{ + "build": { + "arduino": { + "ldscript": "nrf52840_s140_v6.ld" + }, + "core": "nRF5", + "cpu": "cortex-m4", + "extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA", + "f_cpu": "64000000L", + "hwids": [ + [ + "0x239A", + "0x8029" + ], + [ + "0x239A", + "0x0029" + ], + [ + "0x239A", + "0x002A" + ], + [ + "0x239A", + "0x802A" + ] + ], + "usb_product": "Muzi Base Duo", + "mcu": "nrf52840", + "variant": "muzi_base_duo", + "bsp": { + "name": "adafruit" + }, + "softdevice": { + "sd_flags": "-DS140", + "sd_name": "s140", + "sd_version": "6.1.1", + "sd_fwid": "0x00B6" + }, + "bootloader": { + "settings_addr": "0xFF000" + } + }, + "connectivity": [ + "bluetooth" + ], + "debug": { + "jlink_device": "nRF52840_xxAA", + "svd_path": "nrf52840.svd" + }, + "frameworks": [ + "arduino" + ], + "name": "Muzi Base Duo", + "upload": { + "maximum_ram_size": 248832, + "maximum_size": 815104, + "speed": 115200, + "protocol": "nrfutil", + "protocols": [ + "jlink", + "nrfjprog", + "nrfutil", + "stlink" + ], + "use_1200bps_touch": true, + "require_upload_port": true, + "wait_for_upload_port": true + }, + "url": "https://muzi.works/products/base-duo", + "vendor": "Muzi Works" +} diff --git a/boards/muzi_base_uno.json b/boards/muzi_base_uno.json new file mode 100644 index 000000000..2867c541f --- /dev/null +++ b/boards/muzi_base_uno.json @@ -0,0 +1,72 @@ +{ + "build": { + "arduino": { + "ldscript": "nrf52840_s140_v6.ld" + }, + "core": "nRF5", + "cpu": "cortex-m4", + "extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA", + "f_cpu": "64000000L", + "hwids": [ + [ + "0x239A", + "0x8029" + ], + [ + "0x239A", + "0x0029" + ], + [ + "0x239A", + "0x002A" + ], + [ + "0x239A", + "0x802A" + ] + ], + "usb_product": "Muzi Base Uno", + "mcu": "nrf52840", + "variant": "muzi_base_uno", + "bsp": { + "name": "adafruit" + }, + "softdevice": { + "sd_flags": "-DS140", + "sd_name": "s140", + "sd_version": "6.1.1", + "sd_fwid": "0x00B6" + }, + "bootloader": { + "settings_addr": "0xFF000" + } + }, + "connectivity": [ + "bluetooth" + ], + "debug": { + "jlink_device": "nRF52840_xxAA", + "svd_path": "nrf52840.svd" + }, + "frameworks": [ + "arduino" + ], + "name": "Muzi Base Uno", + "upload": { + "maximum_ram_size": 248832, + "maximum_size": 815104, + "speed": 115200, + "protocol": "nrfutil", + "protocols": [ + "jlink", + "nrfjprog", + "nrfutil", + "stlink" + ], + "use_1200bps_touch": true, + "require_upload_port": true, + "wait_for_upload_port": true + }, + "url": "https://muzi.works/products/base-uno", + "vendor": "Muzi Works" +} diff --git a/variants/muzi_base_duo/platformio.ini b/variants/muzi_base_duo/platformio.ini new file mode 100644 index 000000000..0b50014f1 --- /dev/null +++ b/variants/muzi_base_duo/platformio.ini @@ -0,0 +1,123 @@ +[muzi_base_duo] +extends = nrf52_base +board = muzi_base_duo +board_build.ldscript = boards/nrf52840_s140_v6.ld +build_flags = ${nrf52_base.build_flags} + ${sensor_base.build_flags} + -I variants/muzi_base_duo + -I variants/muzi_base_uno + -D MUZI_BASE_DUO + -D NRF52_POWER_MANAGEMENT + -D PIN_BOARD_SCL=14 + -D PIN_BOARD_SDA=13 + -D PIN_GPS_TX=PIN_SERIAL1_RX + -D PIN_GPS_RX=PIN_SERIAL1_TX + -D PIN_GPS_EN=-1 + -D PIN_OLED_RESET=-1 + -D RADIO_CLASS=CustomLR1110 + -D WRAPPER_CLASS=CustomLR1110Wrapper + -D LORA_TX_POWER=20 + -D RX_BOOSTED_GAIN=true + -D LR11X0_DIO3_TCXO_VOLTAGE=1.8 +build_src_filter = ${nrf52_base.build_src_filter} + +<../variants/muzi_base_uno/variant.cpp> + +<../variants/muzi_base_uno/MuziBaseUnoBoard.cpp> + +<../variants/muzi_base_duo> + + + + + + +lib_deps = + ${nrf52_base.lib_deps} + ${sensor_base.lib_deps} + adafruit/Adafruit SSD1306 @ ^2.5.13 + +[env:Muzi_Base_Duo_repeater] +extends = muzi_base_duo +build_flags = + ${muzi_base_duo.build_flags} + -D ADVERT_NAME='"Muzi Base Duo Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${muzi_base_duo.build_src_filter} + +<../examples/simple_repeater> + +[env:Muzi_Base_Duo_repeater_oled] +extends = muzi_base_duo +build_flags = + ${muzi_base_duo.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D PIN_USER_BTN=9 + -D ADVERT_NAME='"Muzi Base Duo Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${muzi_base_duo.build_src_filter} + + + +<../examples/simple_repeater> + +[env:Muzi_Base_Duo_companion_radio_usb] +extends = muzi_base_duo +board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld +board_upload.maximum_size = 712704 +build_flags = + ${muzi_base_duo.build_flags} + -I examples/companion_radio/ui-new + -D PIN_USER_BTN=9 + -D DISPLAY_CLASS=SSD1306Display + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 +; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 +; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 +build_src_filter = ${muzi_base_duo.build_src_filter} + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${muzi_base_duo.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Muzi_Base_Duo_companion_radio_ble] +extends = muzi_base_duo +board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld +board_upload.maximum_size = 712704 +build_flags = + ${muzi_base_duo.build_flags} + -I examples/companion_radio/ui-new + -D PIN_USER_BTN=9 + -D DISPLAY_CLASS=SSD1306Display + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${muzi_base_duo.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${muzi_base_duo.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Muzi_Base_Duo_room_server] +extends = muzi_base_duo +build_flags = + ${muzi_base_duo.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D PIN_USER_BTN=9 + -D ADVERT_NAME='"Muzi Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${muzi_base_duo.build_src_filter} + + + +<../examples/simple_room_server> diff --git a/variants/muzi_base_duo/target.cpp b/variants/muzi_base_duo/target.cpp new file mode 100644 index 000000000..98da46eed --- /dev/null +++ b/variants/muzi_base_duo/target.cpp @@ -0,0 +1,82 @@ +#include +#include "target.h" +#include + +MuziBaseUnoBoard board; + +#ifndef PIN_USER_BTN + #define PIN_USER_BTN (-1) +#endif + +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true, true); +#endif + +#ifndef LORA_CR + #define LORA_CR 5 +#endif + +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); + +WRAPPER_CLASS radio_driver(radio, board); + +VolatileRTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); + +#if ENV_INCLUDE_GPS + #include + MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); + EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); +#else + EnvironmentSensorManager sensors; +#endif + +bool radio_init() { + rtc_clock.begin(Wire); + +#ifdef LR11X0_DIO3_TCXO_VOLTAGE + float tcxo = LR11X0_DIO3_TCXO_VOLTAGE; +#else + float tcxo = 1.8f; +#endif + + SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI); + SPI.begin(); + + int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_LR11X0_LORA_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo); + if (status != RADIOLIB_ERR_NONE) { + Serial.print("ERROR: radio init failed: "); + Serial.println(status); + return false; + } + + radio.setCRC(2); + radio.explicitHeader(); + +#ifdef RX_BOOSTED_GAIN + radio.setRxBoostedGainMode(RX_BOOSTED_GAIN); +#endif + + return true; +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(int8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} diff --git a/variants/muzi_base_duo/target.h b/variants/muzi_base_duo/target.h new file mode 100644 index 000000000..d76c6039e --- /dev/null +++ b/variants/muzi_base_duo/target.h @@ -0,0 +1,27 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include + +#ifdef DISPLAY_CLASS + #include + extern DISPLAY_CLASS display; + #include + extern MomentaryButton user_btn; +#endif + +extern MuziBaseUnoBoard board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern EnvironmentSensorManager sensors; + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(int8_t dbm); +mesh::LocalIdentity radio_new_identity(); diff --git a/variants/muzi_base_uno/MuziBaseUnoBoard.cpp b/variants/muzi_base_uno/MuziBaseUnoBoard.cpp new file mode 100644 index 000000000..0d4e8092d --- /dev/null +++ b/variants/muzi_base_uno/MuziBaseUnoBoard.cpp @@ -0,0 +1,48 @@ +#include +#include + +#include "MuziBaseUnoBoard.h" + +#ifdef NRF52_POWER_MANAGEMENT +const PowerMgtConfig power_config = { + .lpcomp_ain_channel = PWRMGT_LPCOMP_AIN, + .lpcomp_refsel = PWRMGT_LPCOMP_REFSEL, + .voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK +}; + +void MuziBaseUnoBoard::initiateShutdown(uint8_t reason) { + // Disable LoRa module power before shutdown + digitalWrite(SX126X_POWER_EN, LOW); + + if (reason == SHUTDOWN_REASON_LOW_VOLTAGE || + reason == SHUTDOWN_REASON_BOOT_PROTECT) { + configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel); + } + + enterSystemOff(reason); +} +#endif // NRF52_POWER_MANAGEMENT + +void MuziBaseUnoBoard::begin() { + NRF52BoardDCDC::begin(); + + pinMode(PIN_VBAT_READ, INPUT); + +#ifdef PIN_USER_BTN + pinMode(PIN_USER_BTN, INPUT_PULLUP); +#endif + +#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL) + Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL); +#endif + + Wire.begin(); + + // Enable SX1262 power (internal to nRFLR1262 module via P1.05) + pinMode(SX126X_POWER_EN, OUTPUT); +#ifdef NRF52_POWER_MANAGEMENT + checkBootVoltage(&power_config); +#endif + digitalWrite(SX126X_POWER_EN, HIGH); + delay(10); // give sx1262 some time to power up +} diff --git a/variants/muzi_base_uno/MuziBaseUnoBoard.h b/variants/muzi_base_uno/MuziBaseUnoBoard.h new file mode 100644 index 000000000..13f440043 --- /dev/null +++ b/variants/muzi_base_uno/MuziBaseUnoBoard.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include + +class MuziBaseUnoBoard : public NRF52BoardDCDC { +protected: +#ifdef NRF52_POWER_MANAGEMENT + void initiateShutdown(uint8_t reason) override; +#endif + +public: + MuziBaseUnoBoard() : NRF52Board("MuziBaseUno_OTA") {} + void begin(); + + #define BATTERY_SAMPLES 8 + + uint16_t getBattMilliVolts() override { + analogReadResolution(12); + analogReference(AR_INTERNAL_3_0); + delay(1); + + uint32_t raw = 0; + for (int i = 0; i < BATTERY_SAMPLES; i++) { + raw += analogRead(PIN_VBAT_READ); + } + raw = raw / BATTERY_SAMPLES; + + // ADC_MULTIPLIER is the voltage divider ratio + return (raw * ADC_MULTIPLIER * AREF_VOLTAGE) / 4.096; + } + + const char* getManufacturerName() const override { + return "Muzi Base Uno"; + } +}; diff --git a/variants/muzi_base_uno/platformio.ini b/variants/muzi_base_uno/platformio.ini new file mode 100644 index 000000000..c3fd69513 --- /dev/null +++ b/variants/muzi_base_uno/platformio.ini @@ -0,0 +1,120 @@ +[muzi_base_uno] +extends = nrf52_base +board = muzi_base_uno +board_build.ldscript = boards/nrf52840_s140_v6.ld +build_flags = ${nrf52_base.build_flags} + ${sensor_base.build_flags} + -I variants/muzi_base_uno + -D MUZI_BASE_UNO + -D NRF52_POWER_MANAGEMENT + -D PIN_BOARD_SCL=14 + -D PIN_BOARD_SDA=13 + -D PIN_GPS_TX=PIN_SERIAL1_RX + -D PIN_GPS_RX=PIN_SERIAL1_TX + -D PIN_GPS_EN=-1 + -D PIN_OLED_RESET=-1 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=20 + -D SX126X_CURRENT_LIMIT=140 + -D SX126X_RX_BOOSTED_GAIN=1 +build_src_filter = ${nrf52_base.build_src_filter} + +<../variants/muzi_base_uno> + + + + + + +lib_deps = + ${nrf52_base.lib_deps} + ${sensor_base.lib_deps} + adafruit/Adafruit SSD1306 @ ^2.5.13 + +[env:Muzi_Base_Uno_repeater] +extends = muzi_base_uno +build_flags = + ${muzi_base_uno.build_flags} + -D ADVERT_NAME='"Muzi Base Uno Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${muzi_base_uno.build_src_filter} + +<../examples/simple_repeater> + +[env:Muzi_Base_Uno_repeater_oled] +extends = muzi_base_uno +build_flags = + ${muzi_base_uno.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D PIN_USER_BTN=9 + -D ADVERT_NAME='"Muzi Base Uno Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${muzi_base_uno.build_src_filter} + + + +<../examples/simple_repeater> + +[env:Muzi_Base_Uno_companion_radio_usb] +extends = muzi_base_uno +board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld +board_upload.maximum_size = 712704 +build_flags = + ${muzi_base_uno.build_flags} + -I examples/companion_radio/ui-new + -D PIN_USER_BTN=9 + -D DISPLAY_CLASS=SSD1306Display + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 +; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 +; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 +build_src_filter = ${muzi_base_uno.build_src_filter} + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${muzi_base_uno.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Muzi_Base_Uno_companion_radio_ble] +extends = muzi_base_uno +board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld +board_upload.maximum_size = 712704 +build_flags = + ${muzi_base_uno.build_flags} + -I examples/companion_radio/ui-new + -D PIN_USER_BTN=9 + -D DISPLAY_CLASS=SSD1306Display + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${muzi_base_uno.build_src_filter} + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${muzi_base_uno.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:Muzi_Base_Uno_room_server] +extends = muzi_base_uno +build_flags = + ${muzi_base_uno.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D PIN_USER_BTN=9 + -D ADVERT_NAME='"Muzi Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${muzi_base_uno.build_src_filter} + + + +<../examples/simple_room_server> diff --git a/variants/muzi_base_uno/target.cpp b/variants/muzi_base_uno/target.cpp new file mode 100644 index 000000000..be5f31f4d --- /dev/null +++ b/variants/muzi_base_uno/target.cpp @@ -0,0 +1,54 @@ +#include +#include "target.h" +#include + +MuziBaseUnoBoard board; + +#ifndef PIN_USER_BTN + #define PIN_USER_BTN (-1) +#endif + +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; + MomentaryButton user_btn(PIN_USER_BTN, 1000, true, true); +#endif + +RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); + +WRAPPER_CLASS radio_driver(radio, board); + +VolatileRTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); + +#if ENV_INCLUDE_GPS + #include + MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1); + EnvironmentSensorManager sensors = EnvironmentSensorManager(nmea); +#else + EnvironmentSensorManager sensors; +#endif + +bool radio_init() { + rtc_clock.begin(Wire); + return radio.std_init(&SPI); +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(int8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); // create new random identity +} diff --git a/variants/muzi_base_uno/target.h b/variants/muzi_base_uno/target.h new file mode 100644 index 000000000..a5eab4614 --- /dev/null +++ b/variants/muzi_base_uno/target.h @@ -0,0 +1,27 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include + +#ifdef DISPLAY_CLASS + #include + extern DISPLAY_CLASS display; + #include + extern MomentaryButton user_btn; +#endif + +extern MuziBaseUnoBoard board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern EnvironmentSensorManager sensors; + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(int8_t dbm); +mesh::LocalIdentity radio_new_identity(); diff --git a/variants/muzi_base_uno/variant.cpp b/variants/muzi_base_uno/variant.cpp new file mode 100644 index 000000000..8cb2105e1 --- /dev/null +++ b/variants/muzi_base_uno/variant.cpp @@ -0,0 +1,46 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "variant.h" + +#include "nrf.h" +#include "wiring_constants.h" +#include "wiring_digital.h" + +const uint32_t g_ADigitalPinMap[] = { + // P0 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, + + // P1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 +}; + +void initVariant() { +#ifdef PIN_LED1 + pinMode(PIN_LED1, OUTPUT); + ledOff(PIN_LED1); +#endif + +#ifdef PIN_LED2 + pinMode(PIN_LED2, OUTPUT); + ledOff(PIN_LED2); +#endif +} diff --git a/variants/muzi_base_uno/variant.h b/variants/muzi_base_uno/variant.h new file mode 100644 index 000000000..b3328a671 --- /dev/null +++ b/variants/muzi_base_uno/variant.h @@ -0,0 +1,176 @@ +/* + Copyright (c) 2014-2015 Arduino LLC. All right reserved. + Copyright (c) 2016 Sandeep Mistry All right reserved. + Copyright (c) 2018, Adafruit Industries (adafruit.com) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _VARIANT_MUZI_BASE_UNO_ +#define _VARIANT_MUZI_BASE_UNO_ + +#define MUZI_BASE_UNO + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (6) +#define NUM_ANALOG_OUTPUTS (0) + +/* + * LEDs + */ +#define PIN_LED1 (35) // P1.03 - Green LED +#define PIN_LED2 (36) // P1.04 - Blue LED + +#define LED_BUILTIN PIN_LED1 +#define LED_GREEN PIN_LED1 +#define LED_BLUE PIN_LED2 +#define LED_STATE_ON HIGH + +/* + * Buttons + */ +#define PIN_BUTTON1 (9) // P0.09 - User button + +/* + * VBAT sensing + */ +#define PIN_VBAT_READ (5) // P0.05 / AIN3 +#define BATTERY_SENSE_RESOLUTION_BITS 12 +#define BATTERY_SENSE_RESOLUTION 4096.0 +#define AREF_VOLTAGE 3.0 +#define VBAT_AR_INTERNAL AR_INTERNAL_3_0 +#define ADC_MULTIPLIER 1.73 +#define ADC_RESOLUTION 14 + +// Power management boot protection threshold (millivolts) +#define PWRMGT_VOLTAGE_BOOTLOCK 3300 // Won't boot below this voltage (mV) +// LPCOMP wake configuration (voltage recovery from SYSTEMOFF) +// AIN3 = P0.05 = PIN_VBAT_READ +#define PWRMGT_LPCOMP_AIN 3 +#define PWRMGT_LPCOMP_REFSEL 4 // 5/8 VDD (~3.13-3.44V) + +/* + * Analog pins + */ +#define PIN_A0 (5) // P0.05 / AIN3 +#define PIN_A1 (31) // P0.31 / AIN7 +#define PIN_A2 (28) // P0.28 / AIN4 +#define PIN_A3 (29) // P0.29 / AIN5 +#define PIN_A4 (30) // P0.30 / AIN6 +#define PIN_A5 (2) // P0.02 / AIN0 +#define PIN_A6 (0xff) +#define PIN_A7 (0xff) + +static const uint8_t A0 = PIN_A0; +static const uint8_t A1 = PIN_A1; +static const uint8_t A2 = PIN_A2; +static const uint8_t A3 = PIN_A3; +static const uint8_t A4 = PIN_A4; +static const uint8_t A5 = PIN_A5; +static const uint8_t A6 = PIN_A6; +static const uint8_t A7 = PIN_A7; + +// Other pins +#define PIN_AREF (2) +#define PIN_NFC1 (9) +#define PIN_NFC2 (10) + +static const uint8_t AREF = PIN_AREF; + +/* + * Serial interfaces + */ +#define PIN_SERIAL1_RX (15) // P0.15 +#define PIN_SERIAL1_TX (16) // P0.16 + +/* + * SPI Interfaces + * + * PIN_SPI_* defines the default/external SPI available on castellated pads. + * The LoRa radio uses P_LORA_* SPI pins (internal to the nRFLR1262 module), + * which are configured by std_init() in the CustomSX1262 driver. + */ +#define SPI_INTERFACES_COUNT 1 + +#define PIN_SPI_MISO (29) // P0.29 - External SPI (castellated pad) +#define PIN_SPI_MOSI (30) // P0.30 - External SPI (castellated pad) +#define PIN_SPI_SCK (3) // P0.03 - External SPI (castellated pad) + +static const uint8_t SS = 26; // P0.26 - External SPI CS (castellated pad) +static const uint8_t MOSI = PIN_SPI_MOSI; +static const uint8_t MISO = PIN_SPI_MISO; +static const uint8_t SCK = PIN_SPI_SCK; + +/* + * LoRa radio module pins (SX1262 internal to nRFLR1262 module) + * + * Pin mapping from Elecrow nRFLR1262 module block diagram: + * SX1262 DIO1/IRQ -> nRF52840 P1.06 (pin 38) + * SX1262 NSS/SS -> nRF52840 P1.12 (pin 44) + * SX1262 SCK -> nRF52840 P1.13 (pin 45) + * SX1262 MOSI -> nRF52840 P1.14 (pin 46) + * SX1262 MISO -> nRF52840 P1.15 (pin 47) + * SX1262 BUSY -> nRF52840 P1.11 (pin 43) + * SX1262 NRESET -> nRF52840 P1.10 (pin 42) + */ +#define P_LORA_DIO_1 (38) // P1.06 - SX1262 DIO1/IRQ +#define P_LORA_NSS (44) // P1.12 - SX1262 NSS +#define P_LORA_SCLK (45) // P1.13 - SX1262 SCK +#define P_LORA_MOSI (46) // P1.14 - SX1262 MOSI +#define P_LORA_MISO (47) // P1.15 - SX1262 MISO +#define P_LORA_BUSY (43) // P1.11 - SX1262 BUSY +#define P_LORA_RESET (42) // P1.10 - SX1262 NRESET + +// SX1262 power enable - P1.05 (pin 37) internal to nRFLR1262 module +#define SX126X_POWER_EN (37) // P1.05 + +#define SX126X_DIO2_AS_RF_SWITCH true +#define SX126X_DIO3_TCXO_VOLTAGE (1.8f) + +/* + * Wire Interfaces (I2C) + */ +#define WIRE_INTERFACES_COUNT 1 + +#define PIN_WIRE_SDA (13) // P0.13 - QWIIC/STEMMA QT SDA +#define PIN_WIRE_SCL (14) // P0.14 - QWIIC/STEMMA QT SCL + +/* + * QSPI Pins - External 2MB QSPI flash on Base board + * Uses standard nRF52840 dedicated QSPI pins + */ +#define PIN_QSPI_SCK (19) // P0.19 +#define PIN_QSPI_CS (17) // P0.17 +#define PIN_QSPI_IO0 (20) // P0.20 +#define PIN_QSPI_IO1 (21) // P0.21 +#define PIN_QSPI_IO2 (22) // P0.22 +#define PIN_QSPI_IO3 (23) // P0.23 + +// On-board 2MB QSPI Flash +#define EXTERNAL_FLASH_DEVICES IS25LP016D +#define EXTERNAL_FLASH_USE_QSPI + +#endif // _VARIANT_MUZI_BASE_UNO_ From 87e8793d3ab6eff8243eebb23c48d73bd7c82c20 Mon Sep 17 00:00:00 2001 From: John Gagliardi Date: Fri, 20 Feb 2026 20:05:26 -0800 Subject: [PATCH 2/2] Add RF switch table for Muzi Base Duo (LR1121) The LR1121 requires a DIO5/DIO6 RF switch table to switch between TX and RX modes. Without it the radio can transmit but is deaf on receive. --- variants/muzi_base_duo/platformio.ini | 2 ++ variants/muzi_base_duo/target.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/variants/muzi_base_duo/platformio.ini b/variants/muzi_base_duo/platformio.ini index 0b50014f1..51ffc103f 100644 --- a/variants/muzi_base_duo/platformio.ini +++ b/variants/muzi_base_duo/platformio.ini @@ -18,6 +18,8 @@ build_flags = ${nrf52_base.build_flags} -D WRAPPER_CLASS=CustomLR1110Wrapper -D LORA_TX_POWER=20 -D RX_BOOSTED_GAIN=true + -D RF_SWITCH_TABLE + -D LR11X0_DIO_AS_RF_SWITCH=true -D LR11X0_DIO3_TCXO_VOLTAGE=1.8 build_src_filter = ${nrf52_base.build_src_filter} +<../variants/muzi_base_uno/variant.cpp> diff --git a/variants/muzi_base_duo/target.cpp b/variants/muzi_base_duo/target.cpp index 98da46eed..5c6cd1efa 100644 --- a/variants/muzi_base_duo/target.cpp +++ b/variants/muzi_base_duo/target.cpp @@ -17,6 +17,28 @@ MuziBaseUnoBoard board; #define LORA_CR 5 #endif +#ifdef RF_SWITCH_TABLE +static const uint32_t rfswitch_dios[Module::RFSWITCH_MAX_PINS] = { + RADIOLIB_LR11X0_DIO5, + RADIOLIB_LR11X0_DIO6, + RADIOLIB_NC, + RADIOLIB_NC, + RADIOLIB_NC +}; + +static const Module::RfSwitchMode_t rfswitch_table[] = { + // mode DIO5 DIO6 + { LR11x0::MODE_STBY, {LOW , LOW }}, + { LR11x0::MODE_RX, {HIGH, LOW }}, + { LR11x0::MODE_TX, {HIGH, HIGH }}, + { LR11x0::MODE_TX_HP, {LOW , HIGH }}, + { LR11x0::MODE_TX_HF, {LOW , LOW }}, + { LR11x0::MODE_GNSS, {LOW , LOW }}, + { LR11x0::MODE_WIFI, {LOW , LOW }}, + END_OF_MODE_TABLE, +}; +#endif + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI); WRAPPER_CLASS radio_driver(radio, board); @@ -54,6 +76,10 @@ bool radio_init() { radio.setCRC(2); radio.explicitHeader(); +#ifdef RF_SWITCH_TABLE + radio.setRfSwitchTable(rfswitch_dios, rfswitch_table); +#endif + #ifdef RX_BOOSTED_GAIN radio.setRxBoostedGainMode(RX_BOOSTED_GAIN); #endif