mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-11-02 19:02:00 +00:00
feat(l2,core): battery curve editor and an L2 feedback round
- Battery curve: NodePrefs batt_curve_mv[11] (voltage at 0..100 %, all zeros = built-in LiPo curve, sentinel 0xC0DE0032). ui-core/Battery.h uses it for every percentage. L1: Settings > System > Batt curve list (Left/Right 10 mV, Reset); L2: Settings > Power > Battery curve chart with draggable points and a line at the cell's voltage. - L2: the Nodes map is the full navigation map plus the nodes, Back returns to Nodes; the favourite button is lit while it is one; pinning to the Home dial uses a thumbtack (fonts regenerated); minimap dots follow the accent; arranging tiles shows where they land while dragging; emoji row in Diagnostics > Font. - Sim hooks: sim_open nodesmap / accent@N / homeedit / battcurve. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
#include "InfoKit.h"
|
||||
#include "../ui-core/Battery.h"
|
||||
// Settings › System › Battery curve: the cell voltage at 0, 10, ... 100 %
|
||||
// (NodePrefs::batt_curve_mv, ui-core/Battery.h). Left/Right move the selected
|
||||
// point 10 mV, kept between its neighbours; the first change copies the
|
||||
// built-in curve in, Reset goes back to it. "Now" shows the cell as read, to
|
||||
// set a point against. Saved on the way out.
|
||||
// Included by UITask.cpp after AutoAdvertScreen.h.
|
||||
|
||||
class BatteryCurveScreen : public UIScreen {
|
||||
UITask* _task;
|
||||
NodePrefs* _prefs;
|
||||
int _sel = 1, _scroll = 0;
|
||||
bool _dirty = false;
|
||||
|
||||
static const int PTS = battery::CURVE_PTS;
|
||||
static const int ROWS = 1 + PTS + 1; // Now, the points, Reset
|
||||
static const int STEP_MV = 10;
|
||||
|
||||
void nudge(int i, int d) {
|
||||
uint16_t* c = _prefs->batt_curve_mv;
|
||||
if (!battery::validCurve(c))
|
||||
for (int k = 0; k < PTS; k++) c[k] = (uint16_t)battery::builtinMvAt(k);
|
||||
const int lo = i > 0 ? c[i - 1] + STEP_MV : battery::CURVE_MIN_MV;
|
||||
const int hi = i < PTS - 1 ? c[i + 1] - STEP_MV : battery::CURVE_MAX_MV;
|
||||
c[i] = (uint16_t)constrain((int)c[i] + d * STEP_MV, lo, hi);
|
||||
_dirty = true;
|
||||
}
|
||||
|
||||
public:
|
||||
BatteryCurveScreen(UITask* task, NodePrefs* prefs) : _task(task), _prefs(prefs) {}
|
||||
|
||||
void onShow() override { _sel = 1; _scroll = 0; _dirty = false; }
|
||||
|
||||
int render(DisplayDriver& display) override {
|
||||
display.setTextSize(1);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
drawScreenHeader(display, "Battery curve");
|
||||
const int mv = _task->getBattMilliVolts();
|
||||
const bool custom = _prefs && battery::validCurve(_prefs->batt_curve_mv);
|
||||
drawList(display, ROWS, _sel, _scroll, [&](int i, int y, bool sel, int reserve) {
|
||||
drawRowSelection(display, y, sel, reserve);
|
||||
char l[12], v[20];
|
||||
if (i == 0) {
|
||||
strcpy(l, "Now");
|
||||
snprintf(v, sizeof(v), "%d mV %d%%", mv, battery::rawPercent(mv));
|
||||
} else if (i <= PTS) {
|
||||
snprintf(l, sizeof(l), "%d%%", (i - 1) * 10);
|
||||
snprintf(v, sizeof(v), "%d mV", battery::curveMvAt(i - 1));
|
||||
} else {
|
||||
strcpy(l, "Reset");
|
||||
strcpy(v, custom ? "" : "LiPo"); // the built-in curve is the one in use
|
||||
}
|
||||
info::valueRow(display, y, l, v, sel, reserve);
|
||||
});
|
||||
return 1000; // "Now" follows the cell
|
||||
}
|
||||
|
||||
bool handleInput(char c) override {
|
||||
if (c == KEY_CANCEL) {
|
||||
_task->savePrefsIfDirty(_dirty);
|
||||
_task->gotoSettingsScreen();
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_UP) { if (_sel > 0) _sel--; return true; }
|
||||
if (c == KEY_DOWN) { if (_sel < ROWS - 1) _sel++; return true; }
|
||||
if (!_prefs) return false;
|
||||
if (_sel >= 1 && _sel <= PTS && (keyIsNext(c) || keyIsPrev(c))) {
|
||||
nudge(_sel - 1, keyIsNext(c) ? 1 : -1);
|
||||
return true;
|
||||
}
|
||||
if (_sel == ROWS - 1 && c == KEY_ENTER && battery::validCurve(_prefs->batt_curve_mv)) {
|
||||
memset(_prefs->batt_curve_mv, 0, sizeof(_prefs->batt_curve_mv));
|
||||
_dirty = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "AccordionList.h"
|
||||
#include "PopupMenu.h" // scope list management's per-row action menu
|
||||
#include "InfoKit.h" // value / switch rows
|
||||
#include "../ui-core/Battery.h" // the Battery curve row
|
||||
|
||||
class SettingsScreen : public UIScreen {
|
||||
UITask* _task;
|
||||
@@ -49,6 +50,7 @@ class SettingsScreen : public UIScreen {
|
||||
SECTION_SYSTEM,
|
||||
DEVICE_NAME,
|
||||
SCHEMA_SYSTEM, // power, units
|
||||
BATT_CURVE,
|
||||
REBOOT,
|
||||
// Keyboard section
|
||||
SECTION_KEYBOARD,
|
||||
@@ -78,6 +80,7 @@ class SettingsScreen : public UIScreen {
|
||||
int _selected = 0; // SettingItem under the cursor, resolved per input/render
|
||||
int _reserve = 0; // right-edge px reserved for the scrollbar (0 when list fits)
|
||||
bool _dirty = false;
|
||||
bool _keep_place = false; // set when opening a screen from a row, so Back lands on it
|
||||
|
||||
AccordionList _acc;
|
||||
static const int NUM_SECTIONS = 8;
|
||||
@@ -512,6 +515,8 @@ class SettingsScreen : public UIScreen {
|
||||
sw("Lock PIN", _task->passwordLockEnabled());
|
||||
} else if (item == DEVICE_NAME) {
|
||||
val("Name", the_mesh.getNodeName());
|
||||
} else if (item == BATT_CURVE) {
|
||||
val("Batt curve", p && battery::validCurve(p->batt_curve_mv) ? "Custom" : "LiPo");
|
||||
} else if (item == REBOOT) {
|
||||
display.print("Reboot"); // action row: Enter reboots this device
|
||||
} else if (item == KEYBOARD_TYPE) {
|
||||
@@ -629,6 +634,8 @@ public:
|
||||
|
||||
|
||||
void onShow() override {
|
||||
const bool keep = _keep_place; // back from a screen opened here: same row
|
||||
_keep_place = false;
|
||||
_dirty = false;
|
||||
_edit_name = false;
|
||||
_scope_mgmt_active = false;
|
||||
@@ -638,7 +645,7 @@ public:
|
||||
_prune_confirm.active = false;
|
||||
_edit_lock_pass = false;
|
||||
_lock_pass_first[0] = '\0';
|
||||
resetList();
|
||||
if (!keep) resetList();
|
||||
_editor.freq.active = false;
|
||||
}
|
||||
|
||||
@@ -981,6 +988,12 @@ public:
|
||||
_scope_mgmt_scroll = 0;
|
||||
return true;
|
||||
}
|
||||
if (_selected == BATT_CURVE && enter) {
|
||||
_keep_place = true; // back from it to this row, not a folded list
|
||||
_task->savePrefsIfDirty(_dirty);
|
||||
_task->gotoBatteryCurve();
|
||||
return true;
|
||||
}
|
||||
if (_selected == REBOOT && enter) {
|
||||
_task->showAlert("Rebooting...", 800);
|
||||
_task->shutdown(true); // flushes prefs/RTC/contacts/trail, then reboots -- single choke point
|
||||
|
||||
@@ -177,6 +177,7 @@ static void fmtAlt(char* buf, int n, float meters, bool imperial) {
|
||||
#include "NearbyScreen.h"
|
||||
#include "DashboardConfigScreen.h"
|
||||
#include "AutoAdvertScreen.h"
|
||||
#include "BatteryCurveScreen.h"
|
||||
#include "LiveShareScreen.h"
|
||||
#include "LocatorScreen.h"
|
||||
#include "TrailScreen.h"
|
||||
@@ -1623,6 +1624,7 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
|
||||
_sensors = sensors;
|
||||
_node_prefs = node_prefs;
|
||||
_kb.prefs = node_prefs;
|
||||
if (node_prefs) battery::useCurve(node_prefs->batt_curve_mv); // Settings > Battery curve
|
||||
uint32_t aoff = autoOffMillis();
|
||||
_auto_off = millis() + (aoff > 0 ? aoff : AUTO_OFF_MILLIS);
|
||||
|
||||
@@ -1719,6 +1721,7 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
|
||||
nearby_screen = new NearbyScreen(this);
|
||||
dashboard_config = new DashboardConfigScreen(this, node_prefs);
|
||||
auto_advert_screen = new AutoAdvertScreen(this, node_prefs);
|
||||
batt_curve_screen = new BatteryCurveScreen(this, node_prefs);
|
||||
live_share_screen = new LiveShareScreen(this, node_prefs);
|
||||
locator_screen = new LocatorScreen(this, node_prefs);
|
||||
trail_screen = new TrailScreen(this, &_core->trail.store());
|
||||
@@ -1853,6 +1856,7 @@ void UITask::gotoMapScreen() {
|
||||
|
||||
void UITask::gotoLocatorScreen() { setCurrScreen(locator_screen); }
|
||||
void UITask::gotoAutoAdvertScreen() { setCurrScreen(auto_advert_screen); }
|
||||
void UITask::gotoBatteryCurve() { setCurrScreen(batt_curve_screen); }
|
||||
|
||||
bool UITask::startPing(const uint8_t* pub_key) {
|
||||
PingEngine::StartResult r = _core->ping.start(pub_key);
|
||||
|
||||
@@ -112,6 +112,7 @@ class UITask : public UITaskBase, public UiCoreHost {
|
||||
UIScreen* nearby_screen = nullptr;
|
||||
UIScreen* dashboard_config = nullptr;
|
||||
UIScreen* auto_advert_screen = nullptr;
|
||||
UIScreen* batt_curve_screen = nullptr;
|
||||
UIScreen* live_share_screen = nullptr;
|
||||
UIScreen* locator_screen = nullptr;
|
||||
UIScreen* trail_screen = nullptr;
|
||||
@@ -287,6 +288,7 @@ public:
|
||||
void gotoNearbyScreen();
|
||||
void gotoDashboardConfig();
|
||||
void gotoAutoAdvertScreen();
|
||||
void gotoBatteryCurve(); // Settings > System > Battery curve
|
||||
void gotoLiveShareScreen();
|
||||
// Live share session control (ui-core/LiveShareEngine.h): restart = new
|
||||
// session (re-announce + fresh clock); clock-only = changed "Stop after".
|
||||
|
||||
Reference in New Issue
Block a user