merge fix/llcc68-long-message-failure into develop
CI / build-debian-src (push) Skipped
CI / package-pio-deps-native-tft (push) Skipped
CI / test-native (push) Skipped
CI / setup (push) Canceled after 0s
CI / version (push) Canceled after 0s
Build Windows Binary / build-Windows (push) Canceled after 0s
Build PortDuino WASM / Build PortDuino WASM (push) Canceled after 0s
CI / check (push) Canceled after 0s
CI / build (push) Canceled after 0s
CI / ci-gate (push) Canceled after 0s
CI / gather-artifacts (esp32) (push) Canceled after 0s
CI / gather-artifacts (esp32c3) (push) Canceled after 0s
CI / gather-artifacts (esp32c6) (push) Canceled after 0s
CI / gather-artifacts (esp32s3) (push) Canceled after 0s
CI / gather-artifacts (nrf52840) (push) Canceled after 0s
CI / gather-artifacts (rp2040) (push) Canceled after 0s
CI / gather-artifacts (rp2350) (push) Canceled after 0s
CI / gather-artifacts (stm32) (push) Canceled after 0s
CI / firmware-size-report (push) Canceled after 0s
CI / size-budget-gate (push) Canceled after 0s
CI / release-artifacts (push) Canceled after 0s
CI / release-firmware (esp32) (push) Canceled after 0s
CI / release-firmware (esp32c3) (push) Canceled after 0s
CI / release-firmware (esp32c6) (push) Canceled after 0s
CI / release-firmware (esp32s3) (push) Canceled after 0s
CI / release-firmware (nrf52840) (push) Canceled after 0s
CI / release-firmware (rp2040) (push) Canceled after 0s
CI / release-firmware (rp2350) (push) Canceled after 0s
CI / release-firmware (stm32) (push) Canceled after 0s
CI / publish-firmware (push) Canceled after 0s
CI / publish-nightly (push) Canceled after 0s
CI / MacOS (15) (push) Canceled after 0s
CI / MacOS (26) (push) Canceled after 0s
Build MacOS Binary / build-MacOS (push) Canceled after 0s
CI / Windows (2025) (push) Canceled after 0s
CI / build-wasm (push) Canceled after 0s
CI / docker (alpine, native-tft, linux/arm64) (push) Canceled after 0s
CI / docker (debian, native-tft, linux/arm64) (push) Canceled after 0s
Build Docker / docker-build (push) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-manifest (release) Canceled after 0s
Package for OpenSUSE Build Service / package-obs (release) Canceled after 0s
Trigger COPR build / build-copr-hook (release) Canceled after 0s
Trigger release workflows upon Publish / publish-release-notes (release) Canceled after 0s
Trigger release workflows upon Publish / bump-version (release) Canceled after 0s
Trigger release workflows upon Publish / build-docker (release) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-debian-amd64 (release) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-debian-arm64 (release) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-debian-armv7 (release) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-debian-riscv64 (release) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-alpine-amd64 (release) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-alpine-arm64 (release) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-alpine-armv7 (release) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-alpine-riscv64 (release) Canceled after 0s
Trigger release workflows upon Publish / package-ppa (jammy) (release) Canceled after 0s
Trigger release workflows upon Publish / package-ppa (noble) (release) Canceled after 0s
Trigger release workflows upon Publish / package-ppa (resolute) (release) Canceled after 0s
Trigger release workflows upon Publish / package-ppa (stonking) (release) Canceled after 0s
Package for Launchpad PPA / build-debian-src (release) Canceled after 0s
Package for Launchpad PPA / package-ppa (release) Canceled after 0s
Trigger release workflows upon Publish / package-obs (release) Canceled after 0s
Package for OpenSUSE Build Service / build-debian-src (release) Canceled after 0s
Trigger release workflows upon Publish / hook-copr (release) Canceled after 0s
Build Docker / docker-build (release) Canceled after 0s
Build Debian Source Package / build-debian-src (release) Canceled after 0s
CI / build-debian-src (push) Skipped
CI / package-pio-deps-native-tft (push) Skipped
CI / test-native (push) Skipped
CI / setup (push) Canceled after 0s
CI / version (push) Canceled after 0s
Build Windows Binary / build-Windows (push) Canceled after 0s
Build PortDuino WASM / Build PortDuino WASM (push) Canceled after 0s
CI / check (push) Canceled after 0s
CI / build (push) Canceled after 0s
CI / ci-gate (push) Canceled after 0s
CI / gather-artifacts (esp32) (push) Canceled after 0s
CI / gather-artifacts (esp32c3) (push) Canceled after 0s
CI / gather-artifacts (esp32c6) (push) Canceled after 0s
CI / gather-artifacts (esp32s3) (push) Canceled after 0s
CI / gather-artifacts (nrf52840) (push) Canceled after 0s
CI / gather-artifacts (rp2040) (push) Canceled after 0s
CI / gather-artifacts (rp2350) (push) Canceled after 0s
CI / gather-artifacts (stm32) (push) Canceled after 0s
CI / firmware-size-report (push) Canceled after 0s
CI / size-budget-gate (push) Canceled after 0s
CI / release-artifacts (push) Canceled after 0s
CI / release-firmware (esp32) (push) Canceled after 0s
CI / release-firmware (esp32c3) (push) Canceled after 0s
CI / release-firmware (esp32c6) (push) Canceled after 0s
CI / release-firmware (esp32s3) (push) Canceled after 0s
CI / release-firmware (nrf52840) (push) Canceled after 0s
CI / release-firmware (rp2040) (push) Canceled after 0s
CI / release-firmware (rp2350) (push) Canceled after 0s
CI / release-firmware (stm32) (push) Canceled after 0s
CI / publish-firmware (push) Canceled after 0s
CI / publish-nightly (push) Canceled after 0s
CI / MacOS (15) (push) Canceled after 0s
CI / MacOS (26) (push) Canceled after 0s
Build MacOS Binary / build-MacOS (push) Canceled after 0s
CI / Windows (2025) (push) Canceled after 0s
CI / build-wasm (push) Canceled after 0s
CI / docker (alpine, native-tft, linux/arm64) (push) Canceled after 0s
CI / docker (debian, native-tft, linux/arm64) (push) Canceled after 0s
Build Docker / docker-build (push) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-manifest (release) Canceled after 0s
Package for OpenSUSE Build Service / package-obs (release) Canceled after 0s
Trigger COPR build / build-copr-hook (release) Canceled after 0s
Trigger release workflows upon Publish / publish-release-notes (release) Canceled after 0s
Trigger release workflows upon Publish / bump-version (release) Canceled after 0s
Trigger release workflows upon Publish / build-docker (release) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-debian-amd64 (release) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-debian-arm64 (release) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-debian-armv7 (release) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-debian-riscv64 (release) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-alpine-amd64 (release) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-alpine-arm64 (release) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-alpine-armv7 (release) Canceled after 0s
Build Docker Multi-Arch Manifest / docker-alpine-riscv64 (release) Canceled after 0s
Trigger release workflows upon Publish / package-ppa (jammy) (release) Canceled after 0s
Trigger release workflows upon Publish / package-ppa (noble) (release) Canceled after 0s
Trigger release workflows upon Publish / package-ppa (resolute) (release) Canceled after 0s
Trigger release workflows upon Publish / package-ppa (stonking) (release) Canceled after 0s
Package for Launchpad PPA / build-debian-src (release) Canceled after 0s
Package for Launchpad PPA / package-ppa (release) Canceled after 0s
Trigger release workflows upon Publish / package-obs (release) Canceled after 0s
Package for OpenSUSE Build Service / build-debian-src (release) Canceled after 0s
Trigger release workflows upon Publish / hook-copr (release) Canceled after 0s
Build Docker / docker-build (release) Canceled after 0s
Build Debian Source Package / build-debian-src (release) Canceled after 0s
This commit is contained in:
8 files changed
+538
-30
No files matched your search
@@ -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 <typename T> bool SX126xInterface<T>::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)
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user