mirror of
https://github.com/ALLFATHER-BV/wadamesh.git
synced 2026-08-27 22:35:07 +00:00
Park the magnetometer when idle, and stop Lua apps ticking into a dark screen
Two answers to "does the compass app matter for battery life", both yes-ish and both now fixed. The driver put the QMC6309 into normal mode at boot and left it there, so it converted continuously from power-on whether or not anything read it: ~1 mA at 100 Hz / OSR 8, forever, on a 2300 mAh battery. It is now parked in suspend after configuration and woken on demand, with m9CompassIdleTick() (called from the M9's existing per-loop branch) suspending it again two seconds after the last read. Waking is a single register write since suspend preserves the configuration; the waking call reports "nothing fresh" and the caller's next poll gets data, which callers already handle because the chip may be absent. Separately, lv_timer_handler() runs unconditionally, so a Lua app's timer kept firing while the display slept -- GPS Compass polled the magnetometer at 10 Hz into a dark screen, which would have held the sensor awake even after the fix above. The host now skips the tick while the screen is off and resumes on wake; dt comes from millis(), so an app sees one long frame rather than a broken clock. Documented on the SDK page. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
08c9f2b822
commit
5a4a5a8ab6
@@ -29,7 +29,8 @@ extern "C" {
|
||||
// to keep this TU decoupled from the 47k-line UITask.cpp).
|
||||
extern const lv_font_t* luaHostFontForSize(int size_class); // 12/14/16 -> g_font_*
|
||||
extern void luaHostToast(const char* msg, int ms); // showAlert passthrough
|
||||
extern bool luaHostBeep(); // notification chime; false = no sounder / muted
|
||||
extern bool luaHostBeep();
|
||||
extern bool luaHostScreenOn(); // false = display asleep; app ticks pause // notification chime; false = no sounder / muted
|
||||
extern fs::FS* luaHostAppFs(); // /apps storage root FS (may be null)
|
||||
extern void luaHostAppPath(char* out, size_t cap, const char* rel); // prefixes the store root
|
||||
extern int luaHostContactAt(int idx, char* name, size_t name_cap, int* type, uint32_t* secs_ago,
|
||||
@@ -893,6 +894,11 @@ int sysBoard(lua_State* L) {
|
||||
void tickTimerCb(lv_timer_t* t) {
|
||||
(void)t;
|
||||
if (!s_h || !s_h->L) return;
|
||||
// Screen asleep: nothing the app draws is visible, and an app polling a
|
||||
// sensor would keep the hardware awake for a dark screen. Skip the tick and
|
||||
// let it resume on wake — dt is derived from millis(), so the app sees the
|
||||
// pause as one long frame rather than a broken clock.
|
||||
if (!luaHostScreenOn()) { s_h->last_tick = 0; return; }
|
||||
uint32_t now = millis();
|
||||
uint32_t dt = s_h->last_tick ? now - s_h->last_tick : 0;
|
||||
s_h->last_tick = now;
|
||||
|
||||
@@ -46532,6 +46532,12 @@ static bool uiDataFsReady() {
|
||||
#if CAP_LUA_APPS
|
||||
// ---- Lua app host bridges (LuaAppHost.cpp externs) ----
|
||||
// The host lives in its own TU; these three shims are its only view of UITask.
|
||||
// A Lua app's timer keeps firing while the screen is off — lv_timer_handler runs
|
||||
// unconditionally — so an app polling a sensor at 10 Hz went on doing it into a
|
||||
// dark screen. Nothing it draws can be seen, so the host skips the tick; the app
|
||||
// resumes on wake and sees the gap through sys.millis() like any other pause.
|
||||
bool luaHostScreenOn() { return g_lv.task ? !g_lv.task->isScreenOff() : true; }
|
||||
|
||||
const lv_font_t* luaHostFontForSize(int size_class) {
|
||||
return size_class <= 12 ? &g_font_12 : size_class <= 14 ? &g_font_14 : &g_font_16;
|
||||
}
|
||||
@@ -52568,6 +52574,9 @@ void UITask::loop() {
|
||||
// the current tree before draining (cheap: sig-compare early-out).
|
||||
if (s_kbd_nav || s_tb_nav) navMaybeRebuild();
|
||||
m9KeyboardPoll();
|
||||
#if defined(HAS_M9_COMPASS)
|
||||
m9CompassIdleTick(); // park the magnetometer when no app is reading it
|
||||
#endif
|
||||
for (int kbi = 0; kbi < 12; ++kbi) {
|
||||
int key = m9KeyboardReadKey();
|
||||
if (key <= 0) break;
|
||||
|
||||
@@ -44,10 +44,14 @@ constexpr uint8_t kCtrl2 = 0x30;
|
||||
// quarter of the lag. Normal mode honours the ODR (≈1 mA at 100 Hz/OSR 8);
|
||||
// continuous mode free-runs at the maximum rate and is not needed here.
|
||||
constexpr uint8_t kCtrl1 = 0x41;
|
||||
// CTRL1 with MODE = 00: suspend. Registers keep their values, so waking is a
|
||||
// single write of kCtrl1 — no reconfiguration.
|
||||
constexpr uint8_t kModeSuspend = 0x40;
|
||||
|
||||
constexpr float kGaussPerLsb = 1.0f / 1000.0f; // ±32 G range
|
||||
constexpr uint32_t kOvflLogEvery = 10000; // ms between overflow log lines
|
||||
constexpr uint32_t kSampleMaxAge = 1000; // ms a cached sample stays valid
|
||||
constexpr uint32_t kIdleSuspendMs = 2000; // no reads for this long -> suspend the chip
|
||||
constexpr uint32_t kReprobeEvery = 2000; // ms between probes while absent
|
||||
constexpr int kMaxBusErrors = 8; // consecutive, before re-probing
|
||||
|
||||
@@ -61,6 +65,11 @@ uint32_t s_sample_ms = 0;
|
||||
bool s_have_sample = false;
|
||||
bool s_ovfl = false;
|
||||
uint32_t s_ovfl_log_ms = 0;
|
||||
// The part measures continuously in normal mode (~1 mA at 100 Hz / OSR 8) —
|
||||
// worth having while an app is reading the compass, pure waste the rest of the
|
||||
// time, which is nearly always. So it is parked in suspend and woken on demand.
|
||||
bool s_awake = false;
|
||||
uint32_t s_last_read_ms = 0;
|
||||
|
||||
bool writeReg(uint8_t reg, uint8_t val) {
|
||||
s_bus->beginTransmission(kAddr);
|
||||
@@ -90,6 +99,7 @@ bool configure() {
|
||||
delay(10);
|
||||
if (!writeReg(kRegCtrl2, kCtrl2)) return false;
|
||||
if (!writeReg(kRegCtrl1, kCtrl1)) return false;
|
||||
s_awake = true;
|
||||
// Read back: one third-party driver (madflight) saw configuration writes not
|
||||
// stick right after power-up and retries — do the same once rather than
|
||||
// trusting the ACK.
|
||||
@@ -124,6 +134,9 @@ bool probe(bool log) {
|
||||
if (log) Serial.println("M9 compass: QMC6309 ok (id=0x90, 100 Hz, +/-32 G, OSR 8, LPF 4)");
|
||||
s_errors = 0;
|
||||
s_have_sample = false;
|
||||
// Nothing is reading it yet: park it rather than burn ~1 mA from boot to the
|
||||
// first app that asks. m9CompassRead() wakes it.
|
||||
if (writeReg(kRegCtrl1, kModeSuspend)) s_awake = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -152,6 +165,17 @@ bool m9CompassRead(float* x, float* y, float* z, bool* overflow) {
|
||||
if (!s_present) return false;
|
||||
}
|
||||
|
||||
s_last_read_ms = now;
|
||||
if (!s_awake) {
|
||||
// Waking costs one register write; the first conversion lands a sample
|
||||
// period later, so this call reports "nothing fresh" and the caller's next
|
||||
// poll gets real data. Callers already handle a miss (the chip may be
|
||||
// absent), so this needs no special case at the other end.
|
||||
if (!writeReg(kRegCtrl1, kCtrl1)) return false;
|
||||
s_awake = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t st = 0;
|
||||
bool ok = readRegs(kRegStatus, &st, 1);
|
||||
if (ok && (st & kStatDrdy)) {
|
||||
@@ -209,4 +233,14 @@ bool m9CompassRead(float* x, float* y, float* z, bool* overflow) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void m9CompassIdleTick() {
|
||||
if (!s_bus || !s_present || !s_awake) return;
|
||||
const uint32_t now = millis();
|
||||
if ((now - s_last_read_ms) < kIdleSuspendMs) return;
|
||||
if (writeReg(kRegCtrl1, kModeSuspend)) {
|
||||
s_awake = false;
|
||||
s_have_sample = false; // whatever is cached is stale by the time we wake
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAS_M9_COMPASS && ESP32
|
||||
|
||||
@@ -44,4 +44,9 @@ bool m9CompassPresent();
|
||||
* compass", but they are not a usable heading. */
|
||||
bool m9CompassRead(float* x_gauss, float* y_gauss, float* z_gauss, bool* overflow = nullptr);
|
||||
|
||||
/** Call periodically (cheap: one comparison until it acts). Suspends the chip
|
||||
* a couple of seconds after the last read, so it only draws its ~1 mA while
|
||||
* something is actually using the compass. The next read wakes it. */
|
||||
void m9CompassIdleTick();
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user