Merge pull request #45 from xJARiD/develop
fix: reduce repeater battery polling
This commit is contained in:
@@ -86,8 +86,7 @@ Legacy dotted aliases are also accepted:
|
|||||||
|
|
||||||
### Board Battery Reporting
|
### Board Battery Reporting
|
||||||
|
|
||||||
- `get battery.reporting`: shows whether board battery reporting is enabled. Support is board-dependent.
|
- On repeater MQTT builds, background battery sampling used for MQTT/status history is rate-limited to about once per minute. Explicit status and telemetry requests still refresh the reading immediately.
|
||||||
- `set battery.reporting on|off`: enables or disables battery voltage reporting on supported boards. This is currently useful for Heltec V3 boards where USB-only power can produce misleading battery readings. If your board needs this too, open an issue and support can be added board-by-board.
|
|
||||||
|
|
||||||
### T-Beam 1W Fan Control
|
### T-Beam 1W Fan Control
|
||||||
|
|
||||||
|
|||||||
@@ -165,6 +165,30 @@ Notes:
|
|||||||
- the refresh buttons load the current value from the repeater
|
- the refresh buttons load the current value from the repeater
|
||||||
- the save buttons send the matching CLI command immediately
|
- the save buttons send the matching CLI command immediately
|
||||||
|
|
||||||
|
## Ghost Node Mode
|
||||||
|
|
||||||
|
Ghost Node Mode is a convenience control on `/app` for a repeater that should stay on Wi-Fi and MQTT, but should not actively behave like another nearby repeater.
|
||||||
|
|
||||||
|
Typical use case:
|
||||||
|
|
||||||
|
- an indoor or colocated MQTT observer where another repeater nearby is already doing the RF relay work
|
||||||
|
- a node you want feeding MQTT, web status, and troubleshooting data without also adding extra repeat traffic or adverts
|
||||||
|
|
||||||
|
When enabled, Ghost Node Mode:
|
||||||
|
|
||||||
|
- turns `repeat` off
|
||||||
|
- sets `advert.interval` to `0`
|
||||||
|
- sets `flood.advert.interval` to `0`
|
||||||
|
- leaves the local web panel and MQTT features running
|
||||||
|
|
||||||
|
When disabled, the panel restores the prior repeat and advert settings if it still knows them from the current browser session. If not, it falls back to:
|
||||||
|
|
||||||
|
- `repeat on`
|
||||||
|
- `advert.interval 60`
|
||||||
|
- `flood.advert.interval 12`
|
||||||
|
|
||||||
|
This mode is useful when you want the device to observe and publish, not to act as an additional RF repeater. It does not create a separate firmware role; it is just a grouped web-panel shortcut for those existing settings.
|
||||||
|
|
||||||
## MQTT Settings
|
## MQTT Settings
|
||||||
|
|
||||||
This section includes:
|
This section includes:
|
||||||
|
|||||||
@@ -28,44 +28,14 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct WebSensorSnapshot {
|
WebSensorSnapshot collectWebSensorSnapshot(mesh::MainBoard& board, SensorManager& sensors, uint16_t battery_mv) {
|
||||||
bool has_supply_voltage = false;
|
|
||||||
float supply_voltage_v = NAN;
|
|
||||||
|
|
||||||
bool has_sensor_temp = false;
|
|
||||||
float sensor_temp_c = NAN;
|
|
||||||
|
|
||||||
bool has_mcu_temp = false;
|
|
||||||
float mcu_temp_c = NAN;
|
|
||||||
|
|
||||||
bool has_humidity = false;
|
|
||||||
float humidity_pct = NAN;
|
|
||||||
|
|
||||||
bool has_pressure = false;
|
|
||||||
float pressure_hpa = NAN;
|
|
||||||
|
|
||||||
bool has_pressure_altitude = false;
|
|
||||||
float pressure_altitude_m = NAN;
|
|
||||||
|
|
||||||
bool has_gps = false;
|
|
||||||
bool gps_enabled = false;
|
|
||||||
bool gps_fix = false;
|
|
||||||
bool has_gps_lat = false;
|
|
||||||
float gps_lat = NAN;
|
|
||||||
bool has_gps_lon = false;
|
|
||||||
float gps_lon = NAN;
|
|
||||||
bool has_gps_altitude = false;
|
|
||||||
float gps_altitude_m = NAN;
|
|
||||||
bool has_satellites = false;
|
|
||||||
long satellites = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
WebSensorSnapshot collectWebSensorSnapshot(mesh::MainBoard& board, SensorManager& sensors) {
|
|
||||||
WebSensorSnapshot snapshot;
|
WebSensorSnapshot snapshot;
|
||||||
|
snapshot.has_battery = true;
|
||||||
|
snapshot.battery_mv = battery_mv;
|
||||||
|
|
||||||
CayenneLPP sensor_telemetry(200);
|
CayenneLPP sensor_telemetry(200);
|
||||||
sensor_telemetry.reset();
|
sensor_telemetry.reset();
|
||||||
sensor_telemetry.addVoltage(TELEM_CHANNEL_SELF, static_cast<float>(board.getBattMilliVolts()) / 1000.0f);
|
sensor_telemetry.addVoltage(TELEM_CHANNEL_SELF, static_cast<float>(battery_mv) / 1000.0f);
|
||||||
sensors.querySensors(0xFF, sensor_telemetry);
|
sensors.querySensors(0xFF, sensor_telemetry);
|
||||||
|
|
||||||
const float board_temp_c = board.getMCUTemperature();
|
const float board_temp_c = board.getMCUTemperature();
|
||||||
@@ -567,7 +537,7 @@ int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t
|
|||||||
|
|
||||||
if (payload[0] == REQ_TYPE_GET_STATUS) { // guests can also access this now
|
if (payload[0] == REQ_TYPE_GET_STATUS) { // guests can also access this now
|
||||||
RepeaterStats stats;
|
RepeaterStats stats;
|
||||||
stats.batt_milli_volts = board.getBattMilliVolts();
|
stats.batt_milli_volts = getBatteryMilliVolts(true);
|
||||||
stats.curr_tx_queue_len = _mgr->getOutboundTotal();
|
stats.curr_tx_queue_len = _mgr->getOutboundTotal();
|
||||||
stats.noise_floor = (int16_t)_radio->getNoiseFloor();
|
stats.noise_floor = (int16_t)_radio->getNoiseFloor();
|
||||||
stats.last_rssi = (int16_t)radio_driver.getLastRSSI();
|
stats.last_rssi = (int16_t)radio_driver.getLastRSSI();
|
||||||
@@ -593,7 +563,7 @@ int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t
|
|||||||
uint8_t perm_mask = ~(payload[1]); // NEW: first reserved byte (of 4), is now inverse mask to apply to permissions
|
uint8_t perm_mask = ~(payload[1]); // NEW: first reserved byte (of 4), is now inverse mask to apply to permissions
|
||||||
|
|
||||||
telemetry.reset();
|
telemetry.reset();
|
||||||
telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)board.getBattMilliVolts() / 1000.0f);
|
telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)getBatteryMilliVolts(true) / 1000.0f);
|
||||||
|
|
||||||
// query other sensors -- target specific
|
// query other sensors -- target specific
|
||||||
if ((sender->permissions & PERM_ACL_ROLE_MASK) == PERM_ACL_GUEST) {
|
if ((sender->permissions & PERM_ACL_ROLE_MASK) == PERM_ACL_GUEST) {
|
||||||
@@ -1219,12 +1189,15 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
|||||||
_archive = nullptr;
|
_archive = nullptr;
|
||||||
uptime_millis = 0;
|
uptime_millis = 0;
|
||||||
next_archive_neighbours_flush_ms = 0;
|
next_archive_neighbours_flush_ms = 0;
|
||||||
|
next_battery_sample_ms = 0;
|
||||||
next_history_sample_ms = 0;
|
next_history_sample_ms = 0;
|
||||||
next_local_advert = next_flood_advert = 0;
|
next_local_advert = next_flood_advert = 0;
|
||||||
dirty_contacts_expiry = 0;
|
dirty_contacts_expiry = 0;
|
||||||
set_radio_at = revert_radio_at = 0;
|
set_radio_at = revert_radio_at = 0;
|
||||||
_logging = false;
|
_logging = false;
|
||||||
_archive_neighbours_dirty = false;
|
_archive_neighbours_dirty = false;
|
||||||
|
_battery_sample_valid = false;
|
||||||
|
_battery_mv_cache = 0;
|
||||||
region_load_active = false;
|
region_load_active = false;
|
||||||
memset(&_stats_state, 0, sizeof(_stats_state));
|
memset(&_stats_state, 0, sizeof(_stats_state));
|
||||||
|
|
||||||
@@ -1267,7 +1240,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
|||||||
_prefs.advert_loc_policy = ADVERT_LOC_PREFS;
|
_prefs.advert_loc_policy = ADVERT_LOC_PREFS;
|
||||||
|
|
||||||
_prefs.adc_multiplier = 0.0f; // 0.0f means use default board multiplier
|
_prefs.adc_multiplier = 0.0f; // 0.0f means use default board multiplier
|
||||||
_prefs.battery_reporting_enabled = 1;
|
_prefs.reserved_290 = 0;
|
||||||
_prefs.fan_mode = 0; // auto
|
_prefs.fan_mode = 0; // auto
|
||||||
_prefs.fan_timeout_secs = 30;
|
_prefs.fan_timeout_secs = 30;
|
||||||
|
|
||||||
@@ -1374,7 +1347,6 @@ void MyMesh::begin(FILESYSTEM *fs, ArchiveStorage* archive) {
|
|||||||
updateFloodAdvertTimer();
|
updateFloodAdvertTimer();
|
||||||
|
|
||||||
board.setAdcMultiplier(_prefs.adc_multiplier);
|
board.setAdcMultiplier(_prefs.adc_multiplier);
|
||||||
board.setBatteryReporting(_prefs.battery_reporting_enabled);
|
|
||||||
#if defined(TBEAM_1W)
|
#if defined(TBEAM_1W)
|
||||||
auto& tbeam1w_board = static_cast<TBeam1WBoard&>(board);
|
auto& tbeam1w_board = static_cast<TBeam1WBoard&>(board);
|
||||||
tbeam1w_board.setFanPostTxHoldMs(static_cast<uint32_t>(_prefs.fan_timeout_secs) * 1000UL);
|
tbeam1w_board.setFanPostTxHoldMs(static_cast<uint32_t>(_prefs.fan_timeout_secs) * 1000UL);
|
||||||
@@ -1399,6 +1371,18 @@ void MyMesh::sendFloodScoped(const TransportKey& scope, mesh::Packet* pkt, uint3
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t MyMesh::getBatteryMilliVolts(bool force_refresh) {
|
||||||
|
constexpr unsigned long kBatterySampleIntervalMs = 60000UL;
|
||||||
|
|
||||||
|
if (force_refresh || !_battery_sample_valid || next_battery_sample_ms == 0 || millisHasNowPassed(next_battery_sample_ms)) {
|
||||||
|
_battery_mv_cache = board.getBattMilliVolts();
|
||||||
|
_battery_sample_valid = true;
|
||||||
|
next_battery_sample_ms = millis() + kBatterySampleIntervalMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _battery_mv_cache;
|
||||||
|
}
|
||||||
|
|
||||||
void MyMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) {
|
void MyMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) {
|
||||||
set_radio_at = futureMillis(2000); // give CLI reply some time to be sent back, before applying temp radio params
|
set_radio_at = futureMillis(2000); // give CLI reply some time to be sent back, before applying temp radio params
|
||||||
pending_freq = freq;
|
pending_freq = freq;
|
||||||
@@ -1534,7 +1518,13 @@ void MyMesh::removeNeighbor(const uint8_t *pubkey, int key_len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MyMesh::formatStatsReply(char *reply, size_t reply_size) {
|
void MyMesh::formatStatsReply(char *reply, size_t reply_size) {
|
||||||
StatsFormatHelper::formatCoreStats(reply, reply_size, board, *_ms, _err_flags, _mgr);
|
snprintf(reply,
|
||||||
|
reply_size,
|
||||||
|
"{\"battery_mv\":%u,\"uptime_secs\":%u,\"errors\":%u,\"queue_len\":%u}",
|
||||||
|
getBatteryMilliVolts(true),
|
||||||
|
_ms->getMillis() / 1000,
|
||||||
|
_err_flags,
|
||||||
|
_mgr->getOutboundTotal());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyMesh::startRegionsLoad() {
|
void MyMesh::startRegionsLoad() {
|
||||||
@@ -1863,13 +1853,14 @@ void MyMesh::updateStatsHistory(unsigned long now_ms) {
|
|||||||
#endif
|
#endif
|
||||||
if (next_history_sample_ms == 0 || millisHasNowPassed(next_history_sample_ms)) {
|
if (next_history_sample_ms == 0 || millisHasNowPassed(next_history_sample_ms)) {
|
||||||
if (!live_stats_headroom_low) {
|
if (!live_stats_headroom_low) {
|
||||||
WebSensorSnapshot sensor_snapshot = collectWebSensorSnapshot(board, sensors);
|
const uint16_t battery_mv = getBatteryMilliVolts();
|
||||||
|
WebSensorSnapshot sensor_snapshot = collectWebSensorSnapshot(board, sensors, battery_mv);
|
||||||
HistorySample sample{};
|
HistorySample sample{};
|
||||||
sample.epoch_secs = getRTCClock()->getCurrentTime();
|
sample.epoch_secs = getRTCClock()->getCurrentTime();
|
||||||
sample.uptime_secs = static_cast<uint32_t>(uptime_millis / 1000);
|
sample.uptime_secs = static_cast<uint32_t>(uptime_millis / 1000);
|
||||||
sample.packets_sent = radio_driver.getPacketsSent();
|
sample.packets_sent = radio_driver.getPacketsSent();
|
||||||
sample.packets_recv = radio_driver.getPacketsRecv();
|
sample.packets_recv = radio_driver.getPacketsRecv();
|
||||||
sample.battery_mv = board.getBattMilliVolts();
|
sample.battery_mv = battery_mv;
|
||||||
sample.queue_len = static_cast<uint16_t>(_mgr->getOutboundTotal());
|
sample.queue_len = static_cast<uint16_t>(_mgr->getOutboundTotal());
|
||||||
sample.error_flags = _err_flags;
|
sample.error_flags = _err_flags;
|
||||||
sample.recv_errors = static_cast<uint16_t>(min<uint32_t>(radio_driver.getPacketsRecvErrors(), 0xFFFF));
|
sample.recv_errors = static_cast<uint16_t>(min<uint32_t>(radio_driver.getPacketsRecvErrors(), 0xFFFF));
|
||||||
@@ -2488,7 +2479,9 @@ bool MyMesh::formatWebStatsSummaryJson(char* reply, size_t reply_size) {
|
|||||||
wifi_signal[sizeof(wifi_signal) - 1] = 0;
|
wifi_signal[sizeof(wifi_signal) - 1] = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
const uint16_t battery_mv = getBatteryMilliVolts(true);
|
||||||
const int battery_pct = board.getBatteryPercent();
|
const int battery_pct = board.getBatteryPercent();
|
||||||
|
const WebSensorSnapshot sensor_snapshot = collectWebSensorSnapshot(board, sensors, battery_mv);
|
||||||
const bool archive_available = (_archive != nullptr) && _archive->isMounted();
|
const bool archive_available = (_archive != nullptr) && _archive->isMounted();
|
||||||
#ifdef WITH_MQTT_UPLINK
|
#ifdef WITH_MQTT_UPLINK
|
||||||
const bool mqtt_connected = mqtt.isAnyBrokerConnected();
|
const bool mqtt_connected = mqtt.isAnyBrokerConnected();
|
||||||
@@ -2544,7 +2537,7 @@ bool MyMesh::formatWebStatsSummaryJson(char* reply, size_t reply_size) {
|
|||||||
archive_type,
|
archive_type,
|
||||||
static_cast<unsigned long long>(_archive != nullptr ? _archive->getTotalBytes() : 0),
|
static_cast<unsigned long long>(_archive != nullptr ? _archive->getTotalBytes() : 0),
|
||||||
static_cast<unsigned long long>(_archive != nullptr ? _archive->getUsedBytes() : 0),
|
static_cast<unsigned long long>(_archive != nullptr ? _archive->getUsedBytes() : 0),
|
||||||
board.getBattMilliVolts(),
|
battery_mv,
|
||||||
battery_pct,
|
battery_pct,
|
||||||
static_cast<unsigned long>(uptime_millis / 1000),
|
static_cast<unsigned long>(uptime_millis / 1000),
|
||||||
_err_flags,
|
_err_flags,
|
||||||
@@ -2594,7 +2587,7 @@ bool MyMesh::formatWebStatsSummaryJson(char* reply, size_t reply_size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
offset += snprintf(&reply[offset], reply_size - offset, ",");
|
offset += snprintf(&reply[offset], reply_size - offset, ",");
|
||||||
if (!appendJsonSensors(reply, reply_size, offset)) {
|
if (!appendJsonSensors(reply, reply_size, offset, sensor_snapshot)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
offset += snprintf(&reply[offset], reply_size - offset, ",");
|
offset += snprintf(&reply[offset], reply_size - offset, ",");
|
||||||
@@ -2634,10 +2627,8 @@ bool MyMesh::formatWebStatsSeriesJson(const char* series, char* reply, size_t re
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MyMesh::appendJsonSensors(char* reply, size_t reply_size, size_t& offset) const {
|
bool MyMesh::appendJsonSensors(char* reply, size_t reply_size, size_t& offset, const WebSensorSnapshot& snapshot) const {
|
||||||
#if defined(ESP_PLATFORM) && WITH_WEB_PANEL
|
#if defined(ESP_PLATFORM) && WITH_WEB_PANEL
|
||||||
WebSensorSnapshot snapshot = collectWebSensorSnapshot(board, sensors);
|
|
||||||
|
|
||||||
bool needs_comma = false;
|
bool needs_comma = false;
|
||||||
const int open_written = snprintf(&reply[offset], reply_size - offset, "\"sensors\":{");
|
const int open_written = snprintf(&reply[offset], reply_size - offset, "\"sensors\":{");
|
||||||
if (open_written < 0 || static_cast<size_t>(open_written) >= (reply_size - offset)) {
|
if (open_written < 0 || static_cast<size_t>(open_written) >= (reply_size - offset)) {
|
||||||
@@ -2776,7 +2767,7 @@ void MyMesh::loop() {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef WITH_MQTT_UPLINK
|
#ifdef WITH_MQTT_UPLINK
|
||||||
MQTTStatusSnapshot mqtt_status{};
|
MQTTStatusSnapshot mqtt_status{};
|
||||||
mqtt_status.battery_mv = static_cast<int>(board.getBattMilliVolts());
|
mqtt_status.battery_mv = static_cast<int>(getBatteryMilliVolts());
|
||||||
mqtt_status.uptime_secs = static_cast<uint32_t>(uptime_millis / 1000);
|
mqtt_status.uptime_secs = static_cast<uint32_t>(uptime_millis / 1000);
|
||||||
mqtt_status.error_flags = _err_flags;
|
mqtt_status.error_flags = _err_flags;
|
||||||
mqtt_status.queue_len = static_cast<uint16_t>(_mgr->getOutboundTotal());
|
mqtt_status.queue_len = static_cast<uint16_t>(_mgr->getOutboundTotal());
|
||||||
|
|||||||
@@ -65,6 +65,41 @@ struct RepeaterStats {
|
|||||||
uint32_t n_recv_errors;
|
uint32_t n_recv_errors;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct WebSensorSnapshot {
|
||||||
|
bool has_battery = false;
|
||||||
|
uint16_t battery_mv = 0;
|
||||||
|
|
||||||
|
bool has_supply_voltage = false;
|
||||||
|
float supply_voltage_v = NAN;
|
||||||
|
|
||||||
|
bool has_sensor_temp = false;
|
||||||
|
float sensor_temp_c = NAN;
|
||||||
|
|
||||||
|
bool has_mcu_temp = false;
|
||||||
|
float mcu_temp_c = NAN;
|
||||||
|
|
||||||
|
bool has_humidity = false;
|
||||||
|
float humidity_pct = NAN;
|
||||||
|
|
||||||
|
bool has_pressure = false;
|
||||||
|
float pressure_hpa = NAN;
|
||||||
|
|
||||||
|
bool has_pressure_altitude = false;
|
||||||
|
float pressure_altitude_m = NAN;
|
||||||
|
|
||||||
|
bool has_gps = false;
|
||||||
|
bool gps_enabled = false;
|
||||||
|
bool gps_fix = false;
|
||||||
|
bool has_gps_lat = false;
|
||||||
|
float gps_lat = NAN;
|
||||||
|
bool has_gps_lon = false;
|
||||||
|
float gps_lon = NAN;
|
||||||
|
bool has_gps_altitude = false;
|
||||||
|
float gps_altitude_m = NAN;
|
||||||
|
bool has_satellites = false;
|
||||||
|
long satellites = 0;
|
||||||
|
};
|
||||||
|
|
||||||
#ifndef MAX_CLIENTS
|
#ifndef MAX_CLIENTS
|
||||||
#define MAX_CLIENTS 32
|
#define MAX_CLIENTS 32
|
||||||
#endif
|
#endif
|
||||||
@@ -94,10 +129,13 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks, public WebPanelComm
|
|||||||
uint32_t last_millis;
|
uint32_t last_millis;
|
||||||
uint64_t uptime_millis;
|
uint64_t uptime_millis;
|
||||||
unsigned long next_archive_neighbours_flush_ms;
|
unsigned long next_archive_neighbours_flush_ms;
|
||||||
|
unsigned long next_battery_sample_ms;
|
||||||
unsigned long next_history_sample_ms;
|
unsigned long next_history_sample_ms;
|
||||||
unsigned long next_local_advert, next_flood_advert;
|
unsigned long next_local_advert, next_flood_advert;
|
||||||
bool _logging;
|
bool _logging;
|
||||||
bool _archive_neighbours_dirty;
|
bool _archive_neighbours_dirty;
|
||||||
|
bool _battery_sample_valid;
|
||||||
|
uint16_t _battery_mv_cache;
|
||||||
NodePrefs _prefs;
|
NodePrefs _prefs;
|
||||||
ClientACL acl;
|
ClientACL acl;
|
||||||
CommonCLI _cli;
|
CommonCLI _cli;
|
||||||
@@ -159,7 +197,8 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks, public WebPanelComm
|
|||||||
void recordStatsEvent(uint8_t type, int16_t value = 0);
|
void recordStatsEvent(uint8_t type, int16_t value = 0);
|
||||||
bool appendJsonEvents(char* reply, size_t reply_size, size_t& offset) const;
|
bool appendJsonEvents(char* reply, size_t reply_size, size_t& offset) const;
|
||||||
bool appendJsonNeighbours(char* reply, size_t reply_size, size_t& offset) const;
|
bool appendJsonNeighbours(char* reply, size_t reply_size, size_t& offset) const;
|
||||||
bool appendJsonSensors(char* reply, size_t reply_size, size_t& offset) const;
|
bool appendJsonSensors(char* reply, size_t reply_size, size_t& offset, const WebSensorSnapshot& snapshot) const;
|
||||||
|
uint16_t getBatteryMilliVolts(bool force_refresh = false);
|
||||||
uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood);
|
uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood);
|
||||||
uint8_t handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data);
|
uint8_t handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data);
|
||||||
uint8_t handleAnonOwnerReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data);
|
uint8_t handleAnonOwnerReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data);
|
||||||
|
|||||||
@@ -652,8 +652,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
|||||||
_prefs.gps_enabled = 0;
|
_prefs.gps_enabled = 0;
|
||||||
_prefs.gps_interval = 0;
|
_prefs.gps_interval = 0;
|
||||||
_prefs.advert_loc_policy = ADVERT_LOC_PREFS;
|
_prefs.advert_loc_policy = ADVERT_LOC_PREFS;
|
||||||
_prefs.battery_reporting_enabled = 1;
|
_prefs.reserved_290 = 0;
|
||||||
|
|
||||||
next_post_idx = 0;
|
next_post_idx = 0;
|
||||||
next_client_idx = 0;
|
next_client_idx = 0;
|
||||||
next_push = 0;
|
next_push = 0;
|
||||||
@@ -699,7 +698,6 @@ void MyMesh::begin(FILESYSTEM *fs) {
|
|||||||
updateFloodAdvertTimer();
|
updateFloodAdvertTimer();
|
||||||
|
|
||||||
board.setAdcMultiplier(_prefs.adc_multiplier);
|
board.setAdcMultiplier(_prefs.adc_multiplier);
|
||||||
board.setBatteryReporting(_prefs.battery_reporting_enabled);
|
|
||||||
|
|
||||||
#if ENV_INCLUDE_GPS == 1
|
#if ENV_INCLUDE_GPS == 1
|
||||||
applyGpsPrefs();
|
applyGpsPrefs();
|
||||||
|
|||||||
@@ -731,7 +731,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise
|
|||||||
_prefs.gps_enabled = 0;
|
_prefs.gps_enabled = 0;
|
||||||
_prefs.gps_interval = 0;
|
_prefs.gps_interval = 0;
|
||||||
_prefs.advert_loc_policy = ADVERT_LOC_PREFS;
|
_prefs.advert_loc_policy = ADVERT_LOC_PREFS;
|
||||||
_prefs.battery_reporting_enabled = 1;
|
_prefs.reserved_290 = 0;
|
||||||
memset(default_scope.key, 0, sizeof(default_scope.key));
|
memset(default_scope.key, 0, sizeof(default_scope.key));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -771,7 +771,6 @@ void SensorMesh::begin(FILESYSTEM* fs) {
|
|||||||
updateFloodAdvertTimer();
|
updateFloodAdvertTimer();
|
||||||
|
|
||||||
board.setAdcMultiplier(_prefs.adc_multiplier);
|
board.setAdcMultiplier(_prefs.adc_multiplier);
|
||||||
board.setBatteryReporting(_prefs.battery_reporting_enabled);
|
|
||||||
|
|
||||||
#if ENV_INCLUDE_GPS == 1
|
#if ENV_INCLUDE_GPS == 1
|
||||||
applyGpsPrefs();
|
applyGpsPrefs();
|
||||||
|
|||||||
@@ -401,3 +401,27 @@ releases:
|
|||||||
area: docs
|
area: docs
|
||||||
text: "Updated the web panel, API, and custom CLI docs to cover the environment summary/trends, stats persistence behaviour, and existing T-Beam 1W fan controls."
|
text: "Updated the web panel, API, and custom CLI docs to cover the environment summary/trends, stats persistence behaviour, and existing T-Beam 1W fan controls."
|
||||||
breaking_changes: []
|
breaking_changes: []
|
||||||
|
|
||||||
|
- track: repeater-mqtt
|
||||||
|
version: "1.3.9"
|
||||||
|
tag: "repeater-mqtt-eastmesh-v1.3.9"
|
||||||
|
date: "2026-04-22"
|
||||||
|
previous_version: "1.3.8"
|
||||||
|
summary: "Reduced unnecessary repeater battery polling by removing hot-loop MQTT sampling and reusing throttled battery reads across stats and web status paths."
|
||||||
|
changes:
|
||||||
|
- type: fixed
|
||||||
|
area: mqtt
|
||||||
|
text: "Stopped rebuilding the MQTT status battery reading from a fresh ADC sample on every repeater loop iteration and switched that path to a throttled cached value instead."
|
||||||
|
- type: changed
|
||||||
|
area: stats
|
||||||
|
text: "Reused a single battery sample across the web stats history and summary builders so one logical stats operation no longer performs multiple immediate battery reads."
|
||||||
|
- type: fixed
|
||||||
|
area: board-support
|
||||||
|
text: "Reduced Heltec V3 battery-sense activity while MQTT and web features are active, while still keeping explicit status and telemetry requests on a fresh reading path."
|
||||||
|
- type: changed
|
||||||
|
area: board-support
|
||||||
|
text: "Removed the obsolete battery reporting toggle path and its stale CLI documentation now that repeater battery sampling is handled correctly without a board-level reporting disable switch."
|
||||||
|
- type: docs
|
||||||
|
area: docs
|
||||||
|
text: "Updated the custom CLI docs to note that repeater MQTT background battery sampling is now rate-limited while explicit status and telemetry requests still refresh immediately."
|
||||||
|
breaking_changes: []
|
||||||
|
|||||||
@@ -44,9 +44,6 @@ namespace mesh {
|
|||||||
class MainBoard {
|
class MainBoard {
|
||||||
public:
|
public:
|
||||||
virtual uint16_t getBattMilliVolts() = 0;
|
virtual uint16_t getBattMilliVolts() = 0;
|
||||||
virtual bool supportsBatteryReporting() const { return false; }
|
|
||||||
virtual bool setBatteryReporting(bool enabled) { (void)enabled; return false; }
|
|
||||||
virtual bool isBatteryReportingEnabled() const { return true; }
|
|
||||||
virtual int getBatteryPercent() { return -1; }
|
virtual int getBatteryPercent() { return -1; }
|
||||||
virtual bool isCharging() { return false; }
|
virtual bool isCharging() { return false; }
|
||||||
virtual bool isVbusPresent() { return false; }
|
virtual bool isVbusPresent() { return false; }
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
|
|||||||
file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
|
file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
|
||||||
file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
|
file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
|
||||||
file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
|
file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
|
||||||
if (file.available() >= (int)sizeof(_prefs->battery_reporting_enabled)) {
|
if (file.available() >= (int)sizeof(_prefs->reserved_290)) {
|
||||||
file.read((uint8_t *)&_prefs->battery_reporting_enabled, sizeof(_prefs->battery_reporting_enabled)); // 290
|
file.read((uint8_t *)&_prefs->reserved_290, sizeof(_prefs->reserved_290)); // 290 reserved
|
||||||
}
|
}
|
||||||
if (file.available() >= (int)sizeof(_prefs->rx_boosted_gain)) {
|
if (file.available() >= (int)sizeof(_prefs->rx_boosted_gain)) {
|
||||||
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 291
|
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 291
|
||||||
@@ -123,7 +123,6 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
|
|||||||
_prefs->bridge_channel = constrain(_prefs->bridge_channel, 0, 14);
|
_prefs->bridge_channel = constrain(_prefs->bridge_channel, 0, 14);
|
||||||
|
|
||||||
_prefs->powersaving_enabled = constrain(_prefs->powersaving_enabled, 0, 1);
|
_prefs->powersaving_enabled = constrain(_prefs->powersaving_enabled, 0, 1);
|
||||||
_prefs->battery_reporting_enabled = constrain(_prefs->battery_reporting_enabled, 0, 1);
|
|
||||||
|
|
||||||
_prefs->gps_enabled = constrain(_prefs->gps_enabled, 0, 1);
|
_prefs->gps_enabled = constrain(_prefs->gps_enabled, 0, 1);
|
||||||
_prefs->advert_loc_policy = constrain(_prefs->advert_loc_policy, 0, 2);
|
_prefs->advert_loc_policy = constrain(_prefs->advert_loc_policy, 0, 2);
|
||||||
@@ -192,7 +191,7 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
|
|||||||
file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
|
file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
|
||||||
file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
|
file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
|
||||||
file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
|
file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
|
||||||
file.write((uint8_t *)&_prefs->battery_reporting_enabled, sizeof(_prefs->battery_reporting_enabled)); // 290
|
file.write((uint8_t *)&_prefs->reserved_290, sizeof(_prefs->reserved_290)); // 290 reserved
|
||||||
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 291
|
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 291
|
||||||
file.write((uint8_t *)&_prefs->fan_mode, sizeof(_prefs->fan_mode)); // 292
|
file.write((uint8_t *)&_prefs->fan_mode, sizeof(_prefs->fan_mode)); // 292
|
||||||
file.write((uint8_t *)&_prefs->fan_timeout_secs, sizeof(_prefs->fan_timeout_secs)); // 293
|
file.write((uint8_t *)&_prefs->fan_timeout_secs, sizeof(_prefs->fan_timeout_secs)); // 293
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ struct NodePrefs { // persisted to file
|
|||||||
uint32_t discovery_mod_timestamp;
|
uint32_t discovery_mod_timestamp;
|
||||||
float adc_multiplier;
|
float adc_multiplier;
|
||||||
char owner_info[120];
|
char owner_info[120];
|
||||||
uint8_t battery_reporting_enabled; // boolean
|
uint8_t reserved_290; // reserved to preserve on-disk prefs layout
|
||||||
uint8_t rx_boosted_gain; // power settings
|
uint8_t rx_boosted_gain; // power settings
|
||||||
uint8_t path_hash_mode; // which path mode to use when sending
|
uint8_t path_hash_mode; // which path mode to use when sending
|
||||||
uint8_t loop_detect;
|
uint8_t loop_detect;
|
||||||
|
|||||||
@@ -22,13 +22,11 @@
|
|||||||
class HeltecV3Board : public ESP32Board {
|
class HeltecV3Board : public ESP32Board {
|
||||||
private:
|
private:
|
||||||
bool adc_active_state;
|
bool adc_active_state;
|
||||||
bool battery_reporting_enabled;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RefCountedDigitalPin periph_power;
|
RefCountedDigitalPin periph_power;
|
||||||
|
|
||||||
HeltecV3Board() : adc_active_state(false),
|
HeltecV3Board() : adc_active_state(false),
|
||||||
battery_reporting_enabled(true),
|
|
||||||
periph_power(PIN_VEXT_EN) { }
|
periph_power(PIN_VEXT_EN) { }
|
||||||
|
|
||||||
void begin() {
|
void begin() {
|
||||||
@@ -83,10 +81,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint16_t getBattMilliVolts() override {
|
uint16_t getBattMilliVolts() override {
|
||||||
if (!battery_reporting_enabled) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
analogReadResolution(10);
|
analogReadResolution(10);
|
||||||
digitalWrite(PIN_ADC_CTRL, adc_active_state);
|
digitalWrite(PIN_ADC_CTRL, adc_active_state);
|
||||||
|
|
||||||
@@ -109,19 +103,6 @@ public:
|
|||||||
return mv;
|
return mv;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool supportsBatteryReporting() const override {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool setBatteryReporting(bool enabled) override {
|
|
||||||
battery_reporting_enabled = enabled;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isBatteryReportingEnabled() const override {
|
|
||||||
return battery_reporting_enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* getManufacturerName() const override {
|
const char* getManufacturerName() const override {
|
||||||
return "Heltec V3";
|
return "Heltec V3";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user