diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 2400a8e03..484b67b86 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -3,6 +3,7 @@ #include "configuration.h" #include "error.h" #include "mesh/NodeDB.h" +#include "meshUtils.h" // for pow_of_2 #ifdef ARCH_PORTDUINO #include "PortduinoGlue.h" #endif @@ -197,12 +198,28 @@ template bool SX126xInterface::reconfigure() // configure publicly accessible settings int err = lora.setSpreadingFactor(sf); - if (err != RADIOLIB_ERR_NONE) - RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + if (err != RADIOLIB_ERR_NONE) { + LOG_ERROR("SX126X setSpreadingFactor(%d) at BW %.1f kHz rejected (%s%d), attempting fallback", sf, bw, radioLibErr, err); + for (uint8_t fallbackSf = sf - 1; fallbackSf >= 5; fallbackSf--) { + err = lora.setSpreadingFactor(fallbackSf); + if (err == RADIOLIB_ERR_NONE) { + LOG_WARN("SX126X fell back to SF%d at BW %.1f kHz (requested SF%d), change modem preset to match", fallbackSf, bw, + sf); + this->sf = fallbackSf; + preambleTimeMsec = preambleLength * (pow_of_2(sf) / bw); + slotTimeMsec = computeSlotTimeMsec(); + break; + } + } + if (err != RADIOLIB_ERR_NONE) + RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + } err = lora.setBandwidth(bw); - if (err != RADIOLIB_ERR_NONE) + if (err != RADIOLIB_ERR_NONE) { + LOG_ERROR("SX126X setBandwidth(%.1f) rejected (%s%d)", bw, radioLibErr, err); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_INVALID_RADIO_SETTING); + } err = lora.setCodingRate(cr); if (err != RADIOLIB_ERR_NONE) diff --git a/variants/esp32c3/diy/moonshine_ESP_C3/platformio.ini b/variants/esp32c3/diy/moonshine_ESP_C3/platformio.ini index 314a7ae6c..217dcb111 100644 --- a/variants/esp32c3/diy/moonshine_ESP_C3/platformio.ini +++ b/variants/esp32c3/diy/moonshine_ESP_C3/platformio.ini @@ -8,4 +8,5 @@ build_flags = -D PRIVATE_HW -D ARDUINO_USB_MODE=1 -D ARDUINO_USB_CDC_ON_BOOT=1 + -D USERPREFS_LORACONFIG_MODEM_PRESET=meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW -I variants/esp32c3/diy/moonshine_ESP_C3 diff --git a/variants/esp32c3/diy/moonshine_ESP_C3/readme.md b/variants/esp32c3/diy/moonshine_ESP_C3/readme.md index 8dc641ca6..545204c08 100644 --- a/variants/esp32c3/diy/moonshine_ESP_C3/readme.md +++ b/variants/esp32c3/diy/moonshine_ESP_C3/readme.md @@ -24,9 +24,9 @@ commenting/uncommenting the `#define` lines at the top of `variant.h`: Only one module should be enabled at a time. -| Module | Chip | Power | `USE_*` | `TX_GAIN_LORA` | `SX126X_MAX_POWER` | Total output | -| ------------ | ------ | ------------ | ------------ | -------------- | ------------------ | --------------- | -| E220-400M30S | LLCC68 | 30 dBm (1 W) | `USE_LLCC68` | 8 | 22 | 22 + 8 = 30 dBm | +| Module | Chip | Power | `USE_*` | `TX_GAIN_LORA` | `SX126X_MAX_POWER` | Total output | +| ------------ | ------ | ------------ | ------------ | -------------- | ------------------ | ---------------- | +| E220-400M30S | LLCC68 | 30 dBm (1 W) | `USE_LLCC68` | 8 | 22 | 22 + 8 = 30 dBm | | E220-400M33S | LLCC68 | 33 dBm (2 W) | `USE_LLCC68` | 11 | 22 | 22 + 11 = 33 dBm | | E22-400M33S | SX1268 | 33 dBm (2 W) | `USE_SX1268` | 25 | 8 | 8 + 25 = 33 dBm | @@ -42,21 +42,47 @@ See `variants/nrf52840/diy/moonshine_NRF52/variant.h` for the same module wired with a more conservative power profile (`SX126X_MAX_POWER = 2`, ~27 dBm output). +### LLCC68 modem preset limitation + +The E220-400M30S and E220-400M33S use the LLCC68 chip, which does **not** +support all spreading-factor/bandwidth combinations: + +| Bandwidth | Max SF | +| --------- | ------ | +| 125 kHz | SF9 | +| 250 kHz | SF10 | +| 500 kHz | SF11 | + +SF12 is never supported on the LLCC68. + +The Meshtastic default preset **LongFast** (BW 250 kHz / SF11) is invalid +for the LLCC68. This variant therefore ships with **MediumSlow** +(BW 250 kHz / SF10) as the default preset via +`USERPREFS_LORACONFIG_MODEM_PRESET`. + +If you switch to the E22-400M33S (SX1268), you can safely change the preset +back to LongFast or any other - the SX1268 supports the full SF/BW range. + +**Note**: All nodes in a mesh must use the same modem preset to communicate. +If other nodes in your network are on LongFast, either switch them to +MediumSlow as well, or use LongTurbo (BW 500 kHz / SF11) which the LLCC68 +also supports. + ## Pin map -| Function | GPIO | Notes | -| ------------- | ---- | -------------------------------------- | -| BUTTON | 9 | BOOT button | -| LED | 0 | Active high (`LED_STATE_ON = 1`) | -| BATTERY_ADC | 1 | 0.5 divider (`ADC_MULTIPLIER = 2.0`) | -| LORA_RXEN | 2 | RF switch RX enable | -| LORA_DIO1 | 3 | IRQ | -| LORA_BUSY | 4 | | -| LORA_RESET | 5 | | -| LORA_MISO | 6 | | -| LORA_MOSI | 7 | | -| LORA_CS | 8 | | -| LORA_SCK | 10 | | +| Function | GPIO | Notes | +| ----------- | ---- | ------------------------------------ | +| BUTTON | 9 | BOOT button | +| LED | 0 | Active high (`LED_STATE_ON = 1`) | +| BATTERY_ADC | 1 | 0.5 divider (`ADC_MULTIPLIER = 2.0`) | +| LORA_RXEN | 2 | RF switch RX enable | +| LORA_DIO1 | 3 | IRQ | +| LORA_BUSY | 4 | | +| LORA_RESET | 5 | | +| LORA_MISO | 6 | | +| LORA_MOSI | 7 | | +| LORA_CS | 8 | | +| LORA_SCK | 10 | | DIO2 drives the module's TXEN pin internally (`SX126X_DIO2_AS_RF_SWITCH`), so `SX126X_TXEN` is left as `RADIOLIB_NC`. `SX126X_RXEN` is driven by GPIO2. diff --git a/variants/nrf52840/diy/moonshine_NRF52_ebyte/variant.h b/variants/nrf52840/diy/moonshine_NRF52_ebyte/variant.h index 3d25736d4..fb2362a76 100644 --- a/variants/nrf52840/diy/moonshine_NRF52_ebyte/variant.h +++ b/variants/nrf52840/diy/moonshine_NRF52_ebyte/variant.h @@ -67,11 +67,11 @@ MOONSHINE NRF52 EBYTE PIN ASSIGNMENT (Ebyte nRF52840 + E22-400M33S) // LED - Bluetooth pairing status indicator #define LED_PAIRING (0 + 15) // P0.15 -#define LED_STATE_ON 1 // State when LED is lit (active high) +#define LED_STATE_ON 1 // State when LED is lit (active high) -// Charge detection (P0.13, active high = charging) +// Charge detection (P0.13, active high = charging, needs internal pull-down) #define EXT_CHRG_DETECT (0 + 13) -#define EXT_CHRG_DETECT_VALUE HIGH +#define EXT_CHRG_DETECT_MODE INPUT_PULLDOWN // Buzzer #define PIN_BUZZER (32 + 11) // P1.11 @@ -95,8 +95,8 @@ MOONSHINE NRF52 EBYTE PIN ASSIGNMENT (Ebyte nRF52840 + E22-400M33S) #define SPI_INTERFACES_COUNT 1 #define PIN_SPI_MISO (32 + 0) // P1.00 -#define PIN_SPI_MOSI (0 + 22) // P0.22 -#define PIN_SPI_SCK (0 + 20) // P0.20 +#define PIN_SPI_MOSI (0 + 22) // P0.22 +#define PIN_SPI_SCK (0 + 20) // P0.20 #define LORA_MISO PIN_SPI_MISO #define LORA_MOSI PIN_SPI_MOSI @@ -113,14 +113,14 @@ MOONSHINE NRF52 EBYTE PIN ASSIGNMENT (Ebyte nRF52840 + E22-400M33S) #define TX_GAIN_LORA 12 // SX126X CONFIG -#define SX126X_CS (0 + 17) // P0.17 FIXME - we really should define LORA_CS instead -#define SX126X_DIO1 (32 + 10) // P1.10 IRQ +#define SX126X_CS (0 + 17) // P0.17 FIXME - we really should define LORA_CS instead +#define SX126X_DIO1 (32 + 10) // P1.10 IRQ #define SX126X_DIO2_AS_RF_SWITCH // Note for E22 modules: DIO2 is not attached internally to TXEN for automatic TX/RX switching, // so it needs connecting externally if it is used in this way -#define SX126X_BUSY (0 + 28) // P0.28 (AIN4) -#define SX126X_RESET (32 + 13) // P1.13 -#define SX126X_RXEN (32 + 9) // P1.09 - E22 RXEN, MCU controls RX/TX RF switch path -#define SX126X_TXEN RADIOLIB_NC // DIO2 controls TXEN directly +#define SX126X_BUSY (0 + 28) // P0.28 (AIN4) +#define SX126X_RESET (32 + 13) // P1.13 +#define SX126X_RXEN (32 + 9) // P1.09 - E22 RXEN, MCU controls RX/TX RF switch path +#define SX126X_TXEN RADIOLIB_NC // DIO2 controls TXEN directly #define SX126X_DIO3_TCXO_VOLTAGE 1.8 #define TCXO_OPTIONAL // make it so that the firmware can try both TCXO and XTAL diff --git a/variants/nrf52840/diy/moonshine_NRF_travelers_core/platformio.ini b/variants/nrf52840/diy/moonshine_NRF_travelers_core/platformio.ini new file mode 100644 index 000000000..e8322a14b --- /dev/null +++ b/variants/nrf52840/diy/moonshine_NRF_travelers_core/platformio.ini @@ -0,0 +1,22 @@ +; Moonshine NRF travelers core - DIY variant (Ebyte nRF52840 + E22-400M33S) +; Uses S140 v6 SoftDevice (matches nice!nano / Adafruit Feather factory bootloader). +; Inherits the default v6 ldscript from [nrf52840_base] (app start at 0x26000). +[env:moonshine_NRF_travelers_core] +extends = nrf52840_base +board = adafruit_feather_nrf52840 +build_flags = ${nrf52840_base.build_flags} + -I variants/nrf52840/diy/moonshine_NRF_travelers_core + -D PRIVATE_HW + -D CONFIG_GPIO_AS_PINRESET + -D MESHTASTIC_EXCLUDE_I2C=1 + -D MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR=1 + -D MESHTASTIC_EXCLUDE_INPUTBROKER=1 + -D MESHTASTIC_EXCLUDE_ACCELEROMETER=1 + -D MESHTASTIC_EXCLUDE_MAGNETOMETER=1 + -D MESHTASTIC_EXCLUDE_GPS=1 + -D MESHTASTIC_EXCLUDE_SCREEN=1 +build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/diy/moonshine_NRF_travelers_core> +debug_tool = jlink +; Upload via USB: pio run -e moonshine_NRF_travelers_core -t upload +; Uses 1200bps touch to enter DFU bootloader, then nrfutil serial DFU. +; Alternative: double-tap reset -> drag .uf2 to USB mass storage diff --git a/variants/nrf52840/diy/moonshine_NRF_travelers_core/readme.md b/variants/nrf52840/diy/moonshine_NRF_travelers_core/readme.md new file mode 100644 index 000000000..618b6b53a --- /dev/null +++ b/variants/nrf52840/diy/moonshine_NRF_travelers_core/readme.md @@ -0,0 +1,147 @@ +# Moonshine NRF travelers core + +DIY Meshtastic node variant based on an Ebyte nRF52840 MCU module and E22-400M33S LoRa module. + +## Hardware + +- **MCU module**: Ebyte nRF52840 (512 KB flash / 128 KB RAM) +- **LF clock**: 32.768 kHz crystal (`USE_LFXO`) +- **LoRa**: E22-400M33S (SX1268 + 33dBm PA, 2W, 433MHz) - `SX126X_MAX_POWER=21`, + `TX_GAIN_LORA=12`. `SX126X_RXEN` (P0.02) is MCU-controlled by RadioLib; TXEN is + driven by DIO2 (`SX126X_DIO2_AS_RF_SWITCH`). TCXO voltage 2.2V with `TCXO_OPTIONAL` + fallback to XTAL. +- **LED**: Bluetooth pairing status on P0.15 (`LED_PAIRING`). Solid on = BLE + connected, slow blink = unpaired, fast blink = pairing, off after 30s idle. +- **Battery ADC**: 0.5 voltage divider (equal resistors) +- **Power button (P1.09) + self-hold latch (P0.05)**: the button supplies power + while pressed. Hold it 2s to power on - firmware then drives P0.05 HIGH to + latch the board on. Hold it 2s again to power off - the firmware saves the + NodeDB first, then drops P0.05 LOW; the board keeps running on button power + until the button is released. Low-battery cutoff (~3.2V) also drops the latch. + Implemented in a dedicated FreeRTOS task (`powerButtonTask` in variant.cpp) - + the nrf52 platform's weak `variant_shutdown`/`variant_nrf52LoopHook` hooks are + inlined away by LTO and cannot be used (see the `noinline` note in + `src/platform/nrf52/main-nrf52.cpp`). +- **USB CDC serial**: enabled by default (nRF52840 built-in USB-ACM). Debug output + and Meshtastic CLI communication go through the USB serial port. +- **I2C**: SDA/SCL pins wired (P0.07/P0.12) but no I2C devices connected. + +## Pin map + +| Function | Pin | Notes | +| ----------- | ----- | ---------------------------------- | +| LED | P0.15 | Bluetooth pairing status | +| CHARGE_DET | P0.13 | Active high (charging) | +| BUZZER | P1.11 | PWM buzzer | +| BATTERY_ADC | P0.03 | AIN1, 0.5 divider | +| POWER_BTN | P1.09 | Power button, active low (to GND) | +| POWER_LATCH | P0.05 | HIGH = keep board powered | +| IIC_SDA | P0.07 | Wired, no device | +| IIC_SCL | P0.12 | Wired, no device | +| SPI_MISO | P0.26 | | +| SPI_MOSI | P0.06 | | +| SPI_SCK | P0.08 | | +| LORA_CS | P0.04 | | +| LORA_DIO1 | P1.13 | IRQ | +| LORA_BUSY | P0.28 | AIN4 (AI4) | +| LORA_RESET | P1.10 | | +| LORA_RXEN | P0.02 | MCU-controlled RXEN; TXEN via DIO2 | + +## Power button + +- **Power on**: press and hold the button ~2s. The `powerButtonTask` FreeRTOS + task detects P1.09 LOW for 2s, then drives P0.05 HIGH to latch the board on. +- **Power off**: hold the button ~2s while running. The task saves the NodeDB + (`nodeDB->saveToDisk()`), then drops P0.05 LOW. The board keeps running on + button power until the button is released; releasing it cuts power. The + normal shutdown sequence (shutdown melody / SystemOff) is intentionally not + used - the task saves first and then just cuts the latch. +- A release is required between power-on and arming power-off, so holding the + button through the power-on latch cannot shut the board down again. +- If the board resets while latched (button released), P1.09 reads HIGH at boot + and `initVariant` re-latches P0.05 immediately - the board stays on through + crashes and soft resets. +- **Low battery cutoff**: the task checks the battery every 10s and, if it reads + below 3.2V for 3 consecutive samples (and no USB power), saves the NodeDB and + drops the latch. The threshold is above the firmware's own 3.1V SDS trigger so + the latch is released before SystemOff could leave it engaged (SystemOff + retains the latch and would drain the battery). + +## Build & Flash + +### Build + +```sh +pio run -e moonshine_NRF_travelers_core +``` + +Uses `PRIVATE_HW` (HardwareModel 255) - no protobuf or `architecture.h` changes +required. + +The build links against `nrf52840_s140_v6.ld`, so the application is linked to +start at `0x26000` and requires the S140 SoftDevice v6.x to be present on the +chip at `0x0`-`0x25FFF`. The build output (`firmware-*.hex` / `.uf2`) contains +**only the application**, not the SoftDevice. + +### Flash via USB (nice!nano) + +The nice!nano comes pre-flashed with the S140 v6 SoftDevice and the Adafruit +UF2 bootloader. No SWD probe is needed - firmware can be uploaded over USB. + +**Method 1: PlatformIO serial DFU (recommended)** + +```sh +pio run -e moonshine_NRF_travelers_core -t upload +``` + +PlatformIO opens the USB CDC serial port at 1200 baud to trigger the DFU +bootloader, then uploads via `nrfutil` serial DFU. The board automatically +resets into the application after upload. + +**Method 2: UF2 drag-and-drop** + +1. Double-tap the Reset button to enter UF2 bootloader mode. A USB mass storage + volume will appear (e.g. `NRF52BOOT`). +2. Copy the `.uf2` file from `.pio/build/moonshine_NRF_travelers_core/firmware-*.uf2` + to the USB volume. +3. The board automatically resets into the application after the file transfer + completes. + +### Flash via SWD (alternative) + +If the SoftDevice or bootloader is missing or corrupted, flash via SWD with +OpenOCD (CMSIS-DAP / J-Link / ST-Link): + +```sh +# Full flow: recover (erase all + unlock) -> SoftDevice -> app -> reset +openocd -f interface/cmsis-dap.cfg -f target/nrf52.cfg \ + -c "init; nrf52_recover; \ + program bin/s140_nrf52_7.3.0_softdevice.hex verify; \ + program .pio/build/moonshine_NRF_travelers_core/firmware-*.hex verify; \ + reset run; exit" +``` + +When the SoftDevice is already on the chip and only the application changed, +skip `nrf52_recover` and the SoftDevice line: + +```sh +openocd -f interface/cmsis-dap.cfg -f target/nrf52.cfg \ + -c "init; \ + program .pio/build/moonshine_NRF_travelers_core/firmware-*.hex verify; \ + reset run; exit" +``` + +## Notes + +- The firmware hex filename contains a commit hash (e.g. `2.8.0.421838f`); use + the actual file produced by `pio run` or a shell glob. +- USB CDC serial is available immediately after boot for debug output and the + Meshtastic Python CLI (`meshtastic --noproto` / `meshtastic --info`). +- During DFU flashing (bootloader reset), P0.05 is not driven, so the latch + relies on button power or the latch circuit's hold capacitance - hold the + power button during USB DFU if the board loses power mid-flash. +- `MESHTASTIC_EXCLUDE_I2C=1` is set because no I2C devices are connected. Remove + this flag from `platformio.ini` if an I2C display or sensor is added later. +- No GPS, screen, accelerometer, magnetometer, or button is configured. Remove + the corresponding `MESHTASTIC_EXCLUDE_*` flags from `platformio.ini` if hardware + is added. diff --git a/variants/nrf52840/diy/moonshine_NRF_travelers_core/variant.cpp b/variants/nrf52840/diy/moonshine_NRF_travelers_core/variant.cpp new file mode 100644 index 000000000..6280d5f26 --- /dev/null +++ b/variants/nrf52840/diy/moonshine_NRF_travelers_core/variant.cpp @@ -0,0 +1,156 @@ +/* + 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 "PowerStatus.h" // powerStatus (battery state) +#include "configuration.h" // millis, MESHTASTIC_LOG_LEVEL_INFO, console +#include "freertosinc.h" // FreeRTOS (xTaskCreate, vTaskDelay) +#include "mesh/NodeDB.h" // nodeDB +#include "mesh/Throttle.h" // Throttle +#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}; + +// Power button state machine (see variant.h). Runs in its own FreeRTOS task +// because the nrf52 weak loop hooks are inlined away by LTO. +static bool powerLatched = false; + +static void logPowerButton(const char *msg) +{ + if (console) + console->log(MESHTASTIC_LOG_LEVEL_INFO, msg); +} + +static void powerButtonPowerOff() +{ + // Save the DB first, then cut the latch. The board keeps running on button + // power until the button is released (no SystemOff in this flow). + nodeDB->saveToDisk(); + if (console) + console->flush(); + nrf_gpio_pin_write(POWER_LATCH_PIN, 0); + powerLatched = false; +} + +static void powerButtonTask(void *arg) +{ + bool releaseSeen = false; // P1.09 must go HIGH once before arming power-off + bool powerOffRequested = false; + uint32_t lowSinceMs = 0; + uint32_t lastBattCheck = 0; + uint8_t lowBattCount = 0; + + while (true) { + bool pressed = !nrf_gpio_pin_read(POWER_BUTTON_PIN); // LOW = pressed + + if (!powerOffRequested) { + if (pressed) { + if (lowSinceMs == 0) + lowSinceMs = millis(); + else if ((uint32_t)(millis() - lowSinceMs) >= 2000) { + if (!powerLatched) { + // Power-on: hold the button 2s to latch the board on + nrf_gpio_pin_write(POWER_LATCH_PIN, 1); + powerLatched = true; + releaseSeen = false; + logPowerButton("Power button: latched on"); + } else if (releaseSeen) { + // Power-off: hold the button 2s, save the DB, cut power + logPowerButton("Power button: powering off"); + powerOffRequested = true; + powerButtonPowerOff(); + } + lowSinceMs = 0; + } + } else { + releaseSeen = true; + lowSinceMs = 0; + } + } + + // Low-battery cutoff: fires above the firmware's 3100mV SDS threshold + // so the latch is dropped before SystemOff would leave it engaged. + if (!powerOffRequested && !Throttle::isWithinTimespanMs(lastBattCheck, 10000)) { + lastBattCheck = millis(); + if (powerStatus && powerStatus->getHasBattery() && !powerStatus->getHasUSB() && + powerStatus->getBatteryVoltageMv() > 0 && powerStatus->getBatteryVoltageMv() < 3200) { + if (++lowBattCount >= 3) { + logPowerButton("Power button: low battery cutoff"); + powerOffRequested = true; + powerButtonPowerOff(); + } + } else { + lowBattCount = 0; + } + } + + vTaskDelay(pdMS_TO_TICKS(100)); + } +} + +void initVariant() +{ + // Configure P0.18 as RESET pin (UICR->PSELRESET). + // nrf52_recover (ERASEALL) clears UICR to 0xFFFFFFFF, disabling reset. + // SystemInit() already handles this via CONFIG_GPIO_AS_PINRESET; this is a + // safety net that runs before SoftDevice is enabled, so NVMC is accessible. + if ((NRF_UICR->PSELRESET[0] != 18) || (NRF_UICR->PSELRESET[1] != 18)) { + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { + } + NRF_UICR->PSELRESET[0] = 18; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { + } + NRF_UICR->PSELRESET[1] = 18; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { + } + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { + } + NVIC_SystemReset(); // Reboot to apply PSELRESET (hardware reconfigures P0.18) + } + + // Power button + latch: button pulls P1.09 to GND, P0.05 HIGH keeps power on + pinMode(POWER_LATCH_PIN, OUTPUT); + digitalWrite(POWER_LATCH_PIN, LOW); + pinMode(POWER_BUTTON_PIN, INPUT_PULLUP); + + // If the button is not pressed at boot, power must already be latched + // (reboot/crash while running on the latch) - re-latch immediately so the + // board survives the reset glitch. + if (digitalRead(POWER_BUTTON_PIN) == HIGH) { + digitalWrite(POWER_LATCH_PIN, HIGH); + powerLatched = true; + } + + // Initialize pairing LED pin (StatusLEDModule only calls digitalWrite, not pinMode) + pinMode(LED_PAIRING, OUTPUT); + digitalWrite(LED_PAIRING, LED_STATE_ON ^ 1); // OFF + + // Power button state machine (created before the scheduler, runs at boot) + xTaskCreate(powerButtonTask, "powerBtn", 2048, NULL, 10, NULL); +} diff --git a/variants/nrf52840/diy/moonshine_NRF_travelers_core/variant.h b/variants/nrf52840/diy/moonshine_NRF_travelers_core/variant.h new file mode 100644 index 000000000..e2febeb6f --- /dev/null +++ b/variants/nrf52840/diy/moonshine_NRF_travelers_core/variant.h @@ -0,0 +1,139 @@ +#ifndef _VARIANT_MOONSHINE_NRF_TRAVELERS_CORE_ +#define _VARIANT_MOONSHINE_NRF_TRAVELERS_CORE_ + +/** Master clock frequency */ +#define VARIANT_MCK (64000000ul) + +#define USE_LFXO // Board uses 32khz crystal for LF + +/*---------------------------------------------------------------------------- + * Headers + *----------------------------------------------------------------------------*/ + +#include "WVariant.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/* +MOONSHINE NRF TRAVELERS CORE PIN ASSIGNMENT (Ebyte nRF52840 + E22-400M33S) + +| Pin | Function | | Pin | Function | +| ----- | ----------- | ------ | ----- | ----------- | +| P0.02 | LORA_RXEN | | P0.15 | LED | +| P0.03 | VBAT_ADC | | P0.26 | SPI_MISO | +| P0.04 | LORA_CS | | P0.28 | LORA_BUSY | +| P0.05 | POWER_LATCH | | P1.09 | POWER_BTN | +| P0.06 | SPI_MOSI | | P1.10 | LORA_RESET | +| P0.07 | IIC_SDA | | P1.11 | BUZZER | +| P0.08 | SPI_SCK | | P1.13 | LORA_DIO1 | +| P0.12 | IIC_SCL | | | | +| P0.13 | CHAG_DET | | | | +*/ + +// Number of pins defined in PinDescription array +#define PINS_COUNT (48) +#define NUM_DIGITAL_PINS (48) +#define NUM_ANALOG_INPUTS (1) +#define NUM_ANALOG_OUTPUTS (0) + +// No dedicated 3V3 enable pin on this board +#define PIN_3V3_EN (-1) + +// Analog pins +#define BATTERY_PIN (0 + 3) // P0.03 / AIN1 Battery ADC +#define ADC_RESOLUTION 14 +#define BATTERY_SENSE_RESOLUTION_BITS 12 +#define BATTERY_SENSE_RESOLUTION 4096.0 +// Definition of milliVolt per LSB => 3.0V ADC range and 12-bit ADC resolution = 3000mV/4096 +#define VBAT_MV_PER_LSB (0.73242188F) +// Voltage divider value => equal resistors divider = 0.5 +#define VBAT_DIVIDER (0.5F) +// Compensation factor for the VBAT divider +#define VBAT_DIVIDER_COMP (2.0F) +// Fixed calculation of milliVolt from compensation value +#define REAL_VBAT_MV_PER_LSB (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB) +#undef AREF_VOLTAGE +#define AREF_VOLTAGE 3.0 +#define VBAT_AR_INTERNAL AR_INTERNAL_3_0 +#define ADC_MULTIPLIER VBAT_DIVIDER_COMP // REAL_VBAT_MV_PER_LSB +#define VBAT_RAW_TO_SCALED(x) (REAL_VBAT_MV_PER_LSB * x) + +// WIRE IC AND IIC PINS +#define WIRE_INTERFACES_COUNT 1 + +#define PIN_WIRE_SDA (0 + 7) // P0.07 +#define PIN_WIRE_SCL (0 + 12) // P0.12 + +// LED - Bluetooth pairing status indicator +#define LED_PAIRING (0 + 15) // P0.15 +#define LED_STATE_ON 1 // State when LED is lit (active high) + +// Charge detection (P0.13, active high = charging) +#define EXT_CHRG_DETECT (0 + 13) +#define EXT_CHRG_DETECT_VALUE HIGH + +// Buzzer +#define PIN_BUZZER (32 + 11) // P1.11 + +// Power button + self-hold latch: the button (pulled to GND) supplies power while +// pressed; firmware drives P0.05 HIGH after a 2s hold to keep the board powered. +#define POWER_BUTTON_PIN (32 + 9) // P1.09 - LOW = pressed +#define POWER_LATCH_PIN (0 + 5) // P0.05 - HIGH = keep power on + +// UART interfaces - no physical serial ports wired +#define PIN_SERIAL1_RX (-1) +#define PIN_SERIAL1_TX (-1) +#define PIN_SERIAL2_RX (-1) +#define PIN_SERIAL2_TX (-1) + +// GPS - no GPS hardware +#define GPS_RX_PIN (-1) +#define GPS_TX_PIN (-1) +#define PIN_GPS_EN (-1) +#define PIN_GPS_PPS (-1) + +// Serial interfaces +#define SPI_INTERFACES_COUNT 1 + +#define PIN_SPI_MISO (0 + 26) // P0.26 +#define PIN_SPI_MOSI (0 + 6) // P0.06 +#define PIN_SPI_SCK (0 + 8) // P0.08 + +#define LORA_MISO PIN_SPI_MISO +#define LORA_MOSI PIN_SPI_MOSI +#define LORA_SCK PIN_SPI_SCK +#define LORA_CS (0 + 4) // P0.04 + +// LORA MODULES +#define USE_SX1268 // E22-400M33S uses SX1268 (433MHz) + +// E22-400M33S: 33dBm/2W module, PA gain varies with power (non-linear) +// Per E22-M UserManual V1.2 power curve: chip 21dBm -> module 33.5dBm (gain ~12.5dB) +// TX_GAIN_LORA=12 at max power point; SX126X_MAX_POWER + TX_GAIN_LORA = 33dBm +#define SX126X_MAX_POWER 21 +#define TX_GAIN_LORA 12 + +// SX126X CONFIG +#define SX126X_CS (0 + 4) // P0.04 FIXME - we really should define LORA_CS instead +#define SX126X_DIO1 (32 + 13) // P1.13 IRQ +#define SX126X_DIO2_AS_RF_SWITCH // Note for E22 modules: DIO2 is not attached internally to TXEN for automatic TX/RX switching, + // so it needs connecting externally if it is used in this way +#define SX126X_BUSY (0 + 28) // P0.28 (AIN4) +#define SX126X_RESET (32 + 10) // P1.10 +#define SX126X_RXEN (0 + 2) // P0.02 - E22 RXEN, MCU controls RX/TX RF switch path +#define SX126X_TXEN RADIOLIB_NC // DIO2 controls TXEN directly + +#define SX126X_DIO3_TCXO_VOLTAGE 2.2 +#define TCXO_OPTIONAL // make it so that the firmware can try both TCXO and XTAL + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif