Make battery alert thresholds configurable

This commit is contained in:
mikecarper committed 2026-05-22 14:34:19 -07:00
1 parent f693bdb212
commit c93d907e85
4 files changed
+88 -8

No files matched your search

+10 -2
View File
@@ -131,15 +131,23 @@ This document provides an overview of CLI commands that can be sent to MeshCore
**Usage:**
- `get battery.alert`
- `set battery.alert <state>`
- `get battery.alert.low`
- `set battery.alert.low <percent>`
- `get battery.alert.critical`
- `set battery.alert.critical <percent>`
**Parameters:**
- `state`: `on` (enable) or `off` (disable)
- `percent`: Battery percentage threshold
**Default:** `off`
**Default thresholds:** `20` for `battery.alert.low`, `10` for `battery.alert.critical`
**Notes:**
- When enabled, sends a `#repeaters` flood text warning if voltage is above `1 V` and the battery estimate is below `20%`.
- Warnings repeat every `24` hours, or every `12` hours below `10%`.
- When enabled, sends a `#repeaters` flood text warning if voltage is above `1 V` and the battery estimate is below `battery.alert.low`.
- Warnings repeat every `24` hours, or every `12` hours below `battery.alert.critical`.
- `battery.alert.critical` must be lower than `battery.alert.low`.
---
+58 -4
View File
@@ -74,8 +74,8 @@
#endif
#define LOW_BATTERY_MIN_VALID_MV 1000
#define LOW_BATTERY_WARN_PERCENT 20
#define LOW_BATTERY_CRITICAL_PERCENT 10
#define LOW_BATTERY_WARN_PERCENT_DEFAULT 20
#define LOW_BATTERY_CRITICAL_PERCENT_DEFAULT 10
#define LOW_BATTERY_CHECK_INTERVAL (60UL * 1000UL)
#define LOW_BATTERY_WARN_INTERVAL (24UL * 60UL * 60UL * 1000UL)
#define LOW_BATTERY_CRITICAL_INTERVAL (12UL * 60UL * 60UL * 1000UL)
@@ -584,6 +584,30 @@ static uint8_t batteryPercentFromMilliVolts(uint16_t batt_mv) {
return (uint8_t)pct;
}
static bool parseBatteryAlertPercent(const char* value, uint8_t min_value, uint8_t max_value, uint8_t& result) {
if (value == NULL || *value == 0) {
return false;
}
uint16_t parsed = 0;
while (*value) {
if (*value < '0' || *value > '9') {
return false;
}
parsed = (uint16_t)(parsed * 10 + (*value - '0'));
if (parsed > max_value) {
return false;
}
value++;
}
if (parsed < min_value) {
return false;
}
result = (uint8_t)parsed;
return true;
}
mesh::Packet* MyMesh::createPacketCopy(const mesh::Packet* packet, const char* caller) {
if (packet == NULL) return NULL;
@@ -2024,6 +2048,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.flood_retry_bridge_enabled = 0;
_prefs.flood_retry_advert_enabled = 0;
_prefs.battery_alert_enabled = 0;
_prefs.battery_alert_low_percent = LOW_BATTERY_WARN_PERCENT_DEFAULT;
_prefs.battery_alert_critical_percent = LOW_BATTERY_CRITICAL_PERCENT_DEFAULT;
_prefs.direct_retry_cr4_snr_x4 = DIRECT_RETRY_CR4_MIN_SNR_X4_DEFAULT;
_prefs.direct_retry_cr5_snr_x4 = DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT;
_prefs.direct_retry_cr7_snr_x4 = DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT;
@@ -2198,12 +2224,12 @@ void MyMesh::checkBatteryAlert() {
uint16_t batt_mv = board.getBattMilliVolts();
uint8_t batt_pct = batteryPercentFromMilliVolts(batt_mv);
if (batt_mv <= LOW_BATTERY_MIN_VALID_MV || batt_pct >= LOW_BATTERY_WARN_PERCENT) {
if (batt_mv <= LOW_BATTERY_MIN_VALID_MV || batt_pct >= _prefs.battery_alert_low_percent) {
battery_alert_sent = false;
return;
}
unsigned long interval = batt_pct < LOW_BATTERY_CRITICAL_PERCENT
unsigned long interval = batt_pct < _prefs.battery_alert_critical_percent
? LOW_BATTERY_CRITICAL_INTERVAL
: LOW_BATTERY_WARN_INTERVAL;
if (battery_alert_sent && !millisHasNowPassed(last_battery_alert_sent + interval)) {
@@ -2630,6 +2656,10 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *
}
} else if (strcmp(command, "get battery.alert") == 0) {
sprintf(reply, "> %s", _prefs.battery_alert_enabled ? "on" : "off");
} else if (strcmp(command, "get battery.alert.low") == 0) {
sprintf(reply, "> %u", (uint32_t)_prefs.battery_alert_low_percent);
} else if (strcmp(command, "get battery.alert.critical") == 0) {
sprintf(reply, "> %u", (uint32_t)_prefs.battery_alert_critical_percent);
} else if (strncmp(command, "set battery.alert ", 18) == 0) {
const char* value = command + 18;
if (strcmp(value, "on") == 0) {
@@ -2645,6 +2675,30 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *
} else {
strcpy(reply, "Err - usage: set battery.alert <on|off>");
}
} else if (strncmp(command, "set battery.alert.low ", 22) == 0) {
uint8_t percent;
if (!parseBatteryAlertPercent(command + 22, 1, 100, percent)) {
strcpy(reply, "Err - usage: set battery.alert.low <1-100>");
} else if (percent <= _prefs.battery_alert_critical_percent) {
strcpy(reply, "Err - low must be greater than critical");
} else {
_prefs.battery_alert_low_percent = percent;
next_battery_alert_check = 0;
savePrefs();
strcpy(reply, "OK");
}
} else if (strncmp(command, "set battery.alert.critical ", 27) == 0) {
uint8_t percent;
if (!parseBatteryAlertPercent(command + 27, 0, 99, percent)) {
strcpy(reply, "Err - usage: set battery.alert.critical <0-99>");
} else if (percent >= _prefs.battery_alert_low_percent) {
strcpy(reply, "Err - critical must be less than low");
} else {
_prefs.battery_alert_critical_percent = percent;
next_battery_alert_check = 0;
savePrefs();
strcpy(reply, "OK");
}
} else if (strncmp(command, "get recent.repeater", 19) == 0
|| strncmp(command, "set recent.repeater", 19) == 0
|| strncmp(command, "clear recent.repeater", 21) == 0
+18 -2
View File
@@ -559,6 +559,12 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
_prefs->battery_alert_enabled = 0;
size_t battery_alert_read = file.read((uint8_t *)&_prefs->battery_alert_enabled,
sizeof(_prefs->battery_alert_enabled)); // 664
_prefs->battery_alert_low_percent = 20;
_prefs->battery_alert_critical_percent = 10;
size_t battery_alert_low_read = file.read((uint8_t *)&_prefs->battery_alert_low_percent,
sizeof(_prefs->battery_alert_low_percent)); // 665
size_t battery_alert_critical_read = file.read((uint8_t *)&_prefs->battery_alert_critical_percent,
sizeof(_prefs->battery_alert_critical_percent)); // 666
// PowerSaving-only prefs stored radio_fem_rxgain at 291, before direct retry timing existed.
if (radio_fem_rxgain_read != sizeof(_prefs->radio_fem_rxgain)
&& legacy_retry_attempts_read == sizeof(legacy_retry_attempts_or_radio_fem_rxgain)
@@ -566,7 +572,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
|| _prefs->direct_retry_timing_magic[1] != DIRECT_RETRY_TIMING_MAGIC_1)) {
_prefs->radio_fem_rxgain = constrain(legacy_retry_attempts_or_radio_fem_rxgain, 0, 1);
}
// next: 664
// next: 667
// sanitise bad pref values
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
@@ -662,6 +668,14 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
} else {
_prefs->battery_alert_enabled = constrain(_prefs->battery_alert_enabled, 0, 1);
}
if (battery_alert_low_read != sizeof(_prefs->battery_alert_low_percent)
|| battery_alert_critical_read != sizeof(_prefs->battery_alert_critical_percent)
|| _prefs->battery_alert_low_percent < 1
|| _prefs->battery_alert_low_percent > 100
|| _prefs->battery_alert_critical_percent >= _prefs->battery_alert_low_percent) {
_prefs->battery_alert_low_percent = 20;
_prefs->battery_alert_critical_percent = 10;
}
file.close();
}
@@ -748,7 +762,9 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
file.write((uint8_t *)&_prefs->direct_retry_cr7_snr_x4, sizeof(_prefs->direct_retry_cr7_snr_x4)); // 662
file.write((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, sizeof(_prefs->direct_retry_cr8_snr_x4)); // 663
file.write((uint8_t *)&_prefs->battery_alert_enabled, sizeof(_prefs->battery_alert_enabled)); // 664
// next: 665
file.write((uint8_t *)&_prefs->battery_alert_low_percent, sizeof(_prefs->battery_alert_low_percent)); // 665
file.write((uint8_t *)&_prefs->battery_alert_critical_percent, sizeof(_prefs->battery_alert_critical_percent)); // 666
// next: 667
file.close();
}
+2
View File
@@ -133,6 +133,8 @@ struct NodePrefs { // persisted to file
int8_t direct_retry_cr7_snr_x4;
int8_t direct_retry_cr8_snr_x4;
uint8_t battery_alert_enabled;
uint8_t battery_alert_low_percent;
uint8_t battery_alert_critical_percent;
};
class CommonCLICallbacks {