diff --git a/variants/nrf52840/diy/moonshine_NRF52_ebyte/platformio.ini b/variants/nrf52840/diy/moonshine_NRF52_ebyte/platformio.ini new file mode 100644 index 000000000..4e102e76f --- /dev/null +++ b/variants/nrf52840/diy/moonshine_NRF52_ebyte/platformio.ini @@ -0,0 +1,22 @@ +; Moonshine NRF52 ebyte - 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_NRF52_ebyte] +extends = nrf52840_base +board = adafruit_feather_nrf52840 +build_flags = ${nrf52840_base.build_flags} + -I variants/nrf52840/diy/moonshine_NRF52_ebyte + -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_NRF52_ebyte> +debug_tool = jlink +; Upload via USB: pio run -e moonshine_NRF52_ebyte -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_NRF52_ebyte/readme.md b/variants/nrf52840/diy/moonshine_NRF52_ebyte/readme.md new file mode 100644 index 000000000..fc9b53524 --- /dev/null +++ b/variants/nrf52840/diy/moonshine_NRF52_ebyte/readme.md @@ -0,0 +1,114 @@ +# Moonshine NRF52 ebyte + +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=8`, + `TX_GAIN_LORA=25`. `SX126X_RXEN` (P1.09) is MCU-controlled by RadioLib; TXEN is + driven by DIO2 (`SX126X_DIO2_AS_RF_SWITCH`). TCXO voltage 1.8V 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) +- **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.08/P0.04) but no I2C devices connected. + +## Pin map + +| Function | Pin | Notes | +| ------------- | ----- | ---------------------------------- | +| LED | P0.15 | Bluetooth pairing status | +| CHARGE_DET | P0.13 | Active high (charging) | +| BUTTON | P0.24 | User button | +| BUZZER | P1.11 | PWM buzzer | +| BATTERY_ADC | P0.03 | AIN1, 0.5 divider | +| IIC_SDA | P0.08 | Wired, no device | +| IIC_SCL | P0.04 | Wired, no device | +| SPI_MISO | P1.00 | | +| SPI_MOSI | P0.22 | | +| SPI_SCK | P0.20 | | +| LORA_CS | P0.17 | | +| LORA_DIO1 | P1.10 | IRQ | +| LORA_BUSY | P0.28 | AIN4 (AI4) | +| LORA_RESET | P1.13 | | +| LORA_RXEN | P1.09 | MCU-controlled RXEN; TXEN via DIO2 | + +## Build & Flash + +### Build + +```sh +pio run -e moonshine_NRF52_ebyte +``` + +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_NRF52_ebyte -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_NRF52_ebyte/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_NRF52_ebyte/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_NRF52_ebyte/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`). +- `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, or magnetometer is configured. Remove the + corresponding `MESHTASTIC_EXCLUDE_*` flags from `platformio.ini` if hardware + is added. diff --git a/variants/nrf52840/diy/moonshine_NRF52_ebyte/variant.cpp b/variants/nrf52840/diy/moonshine_NRF52_ebyte/variant.cpp new file mode 100644 index 000000000..0a566b708 --- /dev/null +++ b/variants/nrf52840/diy/moonshine_NRF52_ebyte/variant.cpp @@ -0,0 +1,64 @@ +/* + 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() +{ + // 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) + } + + // Initialize pairing LED pin (StatusLEDModule only calls digitalWrite, not pinMode) + pinMode(LED_PAIRING, OUTPUT); + digitalWrite(LED_PAIRING, LED_STATE_ON ^ 1); // OFF +} + +void variant_shutdown() +{ + // Turn off pairing LED + nrf_gpio_pin_write(LED_PAIRING, LED_STATE_ON ^ 1); + + nrf_gpio_cfg_input(BUTTON_PIN, NRF_GPIO_PIN_PULLUP); // Enable internal pull-up on the button pin + nrf_gpio_pin_sense_t sense = NRF_GPIO_PIN_SENSE_LOW; // Configure SENSE signal on low edge + nrf_gpio_cfg_sense_set(BUTTON_PIN, sense); // Apply SENSE to wake up the device from the deep sleep +} diff --git a/variants/nrf52840/diy/moonshine_NRF52_ebyte/variant.h b/variants/nrf52840/diy/moonshine_NRF52_ebyte/variant.h new file mode 100644 index 000000000..3d25736d4 --- /dev/null +++ b/variants/nrf52840/diy/moonshine_NRF52_ebyte/variant.h @@ -0,0 +1,136 @@ +#ifndef _VARIANT_MOONSHINE_NRF52_EBYTE_ +#define _VARIANT_MOONSHINE_NRF52_EBYTE_ + +/** 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 NRF52 EBYTE PIN ASSIGNMENT (Ebyte nRF52840 + E22-400M33S) + +| Pin | Function | | Pin | Function | +| ----- | ----------- | ------ | ----- | ----------- | +| P0.03 | VBAT_ADC | | P0.22 | SPI_MOSI | +| P0.04 | IIC_SCL | | P0.24 | BUTTON | +| P0.08 | IIC_SDA | | P0.28 | LORA_BUSY | +| P0.13 | CHAG_DET | | P1.00 | SPI_MISO | +| P0.15 | LED | | P1.09 | LORA_RXEN | +| P0.17 | LORA_CS | | P1.10 | LORA_DIO1 | +| P0.20 | SPI_SCK | | P1.11 | BUZZER | +| | | | P1.13 | LORA_RESET | +*/ + +// 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 + 8) // P0.08 +#define PIN_WIRE_SCL (0 + 4) // P0.04 + +// 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 + +// Button +#define BUTTON_PIN (0 + 24) // P0.24 + +// 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 (32 + 0) // P1.00 +#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 +#define LORA_SCK PIN_SPI_SCK +#define LORA_CS (0 + 17) // P0.17 + +// 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 + 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_DIO3_TCXO_VOLTAGE 1.8 +#define TCXO_OPTIONAL // make it so that the firmware can try both TCXO and XTAL + +#ifdef __cplusplus +} +#endif + +/*---------------------------------------------------------------------------- + * Arduino objects - C++ only + *----------------------------------------------------------------------------*/ + +#endif