fix: add battery reporting toggle for Heltec V3 and clean up VS Code metadata

Этот коммит содержится в:
Jared Dohrman
2026-04-11 18:14:30 +10:00
родитель 6680e53ef5
Коммит 4f4964e454
11 изменённых файлов: 160 добавлений и 7 удалений
+82
Просмотреть файл
@@ -0,0 +1,82 @@
name: Feature / Support Request
description: Request a feature or report a board-specific support need.
title: "[Feature/Support]: "
labels:
- enhancement
body:
- type: markdown
attributes:
value: |
Use this form for feature requests and board-specific support needs.
- type: textarea
id: summary
attributes:
label: Summary
description: What do you want to add or what do you need help with?
placeholder: Briefly describe the request or issue.
validations:
required: true
- type: input
id: board
attributes:
label: Board
description: Board model and revision, if known.
placeholder: e.g. Heltec WiFi LoRa 32 V3.2
validations:
required: false
- type: input
id: firmware
attributes:
label: Firmware Target
description: Firmware environment or binary name, if known.
placeholder: e.g. Heltec_v3_repeater_mqtt
validations:
required: false
- type: input
id: power
attributes:
label: Power Source
description: How is the board powered?
placeholder: e.g. USB only, LiPo only, USB + LiPo
validations:
required: false
- type: textarea
id: behavior
attributes:
label: Current Behavior
description: What is happening now?
placeholder: Describe the current behavior, readings, LEDs, errors, etc.
validations:
required: false
- type: textarea
id: expected
attributes:
label: Expected Behavior
description: What did you expect instead?
placeholder: Describe the expected result.
validations:
required: false
- type: textarea
id: comparison
attributes:
label: Comparison
description: If relevant, does upstream MeshCore or another firmware behave differently?
placeholder: Include repo/branch/target details if you compared multiple builds.
validations:
required: false
- type: textarea
id: logs
attributes:
label: Logs Or Screenshots
description: Paste logs or attach screenshots/photos if helpful.
placeholder: Add CLI output, boot logs, screenshots, or photos here.
validations:
required: false
+1
Просмотреть файл
@@ -145,6 +145,7 @@ uv run --group docs zensical build
- CLI controls for: - CLI controls for:
- WiFi credentials - WiFi credentials
- WiFi powersaving - WiFi powersaving
- board battery reporting on supported targets
- MQTT endpoint enablement - MQTT endpoint enablement
- MQTT packet and raw publishing - MQTT packet and raw publishing
- owner public key and email - owner public key and email
+10
Просмотреть файл
@@ -102,6 +102,16 @@ Legacy dotted aliases are also accepted:
- `stats-packets` - `stats-packets`
- shows packet receive/send totals, flood/direct breakdown, and receive errors - shows packet receive/send totals, flood/direct breakdown, and receive errors
### Board Battery Reporting
- `get battery.reporting`
- shows whether board battery reporting is enabled
- support is board-dependent
- `set battery.reporting on|off`
- enables or disables battery voltage reporting on supported boards
- 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
## Web Panel Allowlisted Commands ## Web Panel Allowlisted Commands
When the repeater web panel is enabled, it only allows a limited command set. When the repeater web panel is enabled, it only allows a limited command set.
+2
Просмотреть файл
@@ -898,6 +898,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;
#if defined(USE_SX1262) || defined(USE_SX1268) #if defined(USE_SX1262) || defined(USE_SX1268)
#ifdef SX126X_RX_BOOSTED_GAIN #ifdef SX126X_RX_BOOSTED_GAIN
@@ -943,6 +944,7 @@ 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();
+2
Просмотреть файл
@@ -631,6 +631,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;
next_post_idx = 0; next_post_idx = 0;
next_client_idx = 0; next_client_idx = 0;
@@ -654,6 +655,7 @@ 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();
+2
Просмотреть файл
@@ -729,6 +729,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;
} }
void SensorMesh::begin(FILESYSTEM* fs) { void SensorMesh::begin(FILESYSTEM* fs) {
@@ -746,6 +747,7 @@ 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();
+3
Просмотреть файл
@@ -44,6 +44,9 @@ 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 float getMCUTemperature() { return NAN; } virtual float getMCUTemperature() { return NAN; }
virtual bool setAdcMultiplier(float multiplier) { return false; }; virtual bool setAdcMultiplier(float multiplier) { return false; };
virtual float getAdcMultiplier() const { return 0.0f; } virtual float getAdcMultiplier() const { return 0.0f; }
+26 -4
Просмотреть файл
@@ -87,8 +87,13 @@ 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
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 if (file.available() >= (int)sizeof(_prefs->battery_reporting_enabled)) {
// next: 291 file.read((uint8_t *)&_prefs->battery_reporting_enabled, sizeof(_prefs->battery_reporting_enabled)); // 290
}
if (file.available() >= (int)sizeof(_prefs->rx_boosted_gain)) {
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 291
}
// next: 292
// sanitise bad pref values // sanitise bad pref values
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
@@ -112,6 +117,7 @@ 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);
@@ -178,8 +184,9 @@ 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->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290 file.write((uint8_t *)&_prefs->battery_reporting_enabled, sizeof(_prefs->battery_reporting_enabled)); // 290
// next: 291 file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 291
// next: 292
file.close(); file.close();
} }
@@ -421,6 +428,12 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
} else { } else {
sprintf(reply, "> %.3f", adc_mult); sprintf(reply, "> %.3f", adc_mult);
} }
} else if (memcmp(config, "battery.reporting", 17) == 0) {
if (_board->supportsBatteryReporting()) {
sprintf(reply, "> %s", _board->isBatteryReportingEnabled() ? "on" : "off");
} else {
strcpy(reply, "Error: unsupported by this board");
}
// Power management commands // Power management commands
} else if (memcmp(config, "pwrmgt.support", 14) == 0) { } else if (memcmp(config, "pwrmgt.support", 14) == 0) {
#ifdef NRF52_POWER_MANAGEMENT #ifdef NRF52_POWER_MANAGEMENT
@@ -717,6 +730,15 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
_prefs->adc_multiplier = 0.0f; _prefs->adc_multiplier = 0.0f;
strcpy(reply, "Error: unsupported by this board"); strcpy(reply, "Error: unsupported by this board");
}; };
} else if (memcmp(config, "battery.reporting ", 18) == 0) {
bool enabled = memcmp(&config[18], "on", 2) == 0;
if (_board->setBatteryReporting(enabled)) {
_prefs->battery_reporting_enabled = enabled ? 1 : 0;
savePrefs();
sprintf(reply, "OK - battery reporting %s", enabled ? "on" : "off");
} else {
strcpy(reply, "Error: unsupported by this board");
}
} else { } else {
sprintf(reply, "unknown config: %s", config); sprintf(reply, "unknown config: %s", config);
} }
+1
Просмотреть файл
@@ -57,6 +57,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 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;
+30 -2
Просмотреть файл
@@ -19,11 +19,14 @@
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() : periph_power(PIN_VEXT_EN) { } HeltecV3Board() : adc_active_state(false),
battery_reporting_enabled(true),
periph_power(PIN_VEXT_EN) { }
void begin() { void begin() {
ESP32Board::begin(); ESP32Board::begin();
@@ -77,6 +80,10 @@ 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);
@@ -88,7 +95,28 @@ public:
digitalWrite(PIN_ADC_CTRL, !adc_active_state); digitalWrite(PIN_ADC_CTRL, !adc_active_state);
return (5.42 * (3.3 / 1024.0) * raw) * 1000; uint16_t mv = (5.42 * (3.3 / 1024.0) * raw) * 1000;
// Heltec V3 USB-powered boards without a battery can bias or float the
// VBAT sense rail, which shows up as an impossible LiPo voltage.
if (mv > 4400) {
return 0;
}
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 {
+1 -1
Просмотреть файл
@@ -376,4 +376,4 @@ build_flags =
build_src_filter = ${Heltec_lora32_v3.build_src_filter} build_src_filter = ${Heltec_lora32_v3.build_src_filter}
+<../examples/kiss_modem/> +<../examples/kiss_modem/>
lib_deps = lib_deps =
${Heltec_lora32_v3.lib_deps} ${Heltec_lora32_v3.lib_deps}