增加开机功能

This commit is contained in:
2026-08-02 12:32:15 +08:00
parent 3d5c3cc536
commit f256499ca3
3 files changed
+138

No files matched your search

@@ -13,6 +13,15 @@ DIY Meshtastic node variant based on an Ebyte nRF52840 MCU module and E22-400M33
- **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.
@@ -25,6 +34,8 @@ DIY Meshtastic node variant based on an Ebyte nRF52840 MCU module and E22-400M33
| 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 | |
@@ -36,6 +47,26 @@ DIY Meshtastic node variant based on an Ebyte nRF52840 MCU module and E22-400M33
| 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
@@ -106,6 +137,9 @@ openocd -f interface/cmsis-dap.cfg -f target/nrf52.cfg \
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
@@ -19,6 +19,11 @@
*/
#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"
@@ -30,6 +35,83 @@ const uint32_t g_ADigitalPinMap[] = {
// 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).
@@ -52,7 +134,23 @@ void initVariant()
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);
}
@@ -24,6 +24,7 @@ MOONSHINE NRF TRAVELERS CORE PIN ASSIGNMENT (Ebyte nRF52840 + E22-400M33S)
| 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 |
@@ -76,6 +77,11 @@ MOONSHINE NRF TRAVELERS CORE PIN ASSIGNMENT (Ebyte nRF52840 + E22-400M33S)
// 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)