mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-01 22:08:19 +00:00
ui: make housekeeping refresh path on-demand
Stop doing UI work nobody asked for. The 5 s housekeeping tick was reading env sensors (I2C, 10-50 ms), the battery ADC (regulator toggle + 8 samples, every 60 s), and re-rendering the display unconditionally — all while the display might be off and nothing on-air had requested any of it. Now: - render_sensors() reads env sensors only when the user is on that page (event-driven, never fires during idle) - battery refresh is lazy on ui_pages_render() with a 30 s freshness guard; explicit ui_set_battery() calls also count - the unconditional OLED rerender from housekeeping is gone; real state changes (messages, BLE, button press) still fire schedule_render() directly Telemetry / stats paths read fresh ADC + sensors on demand and were never using the UI cache, so over-the-air consumers are unaffected.
This commit is contained in:
@@ -100,3 +100,4 @@ zephcore/SX126X_LBT_RX_DEBUG_HANDOFF.md
|
||||
PSRAM_HANDOFF.md
|
||||
BLE_AUDIT_INDEX.md
|
||||
POWER_AUDIT_HANDOFF.md
|
||||
POWER_AUDIT_INDEX.md
|
||||
|
||||
@@ -231,13 +231,8 @@ extern "C" void mesh_housekeeping_ui_refresh(void)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Battery voltage changes slowly — read every ~60s (12 × 5s housekeeping)
|
||||
* instead of every 5s. Saves power (regulator toggle + ADC) and log noise. */
|
||||
static uint8_t batt_counter;
|
||||
if (++batt_counter >= 12) {
|
||||
batt_counter = 0;
|
||||
ui_set_battery(s_board->getBattMilliVolts(), 0);
|
||||
}
|
||||
/* Battery is now refreshed lazily from ui_pages_render() with a 30 s
|
||||
* freshness guard — no periodic ADC fire here. */
|
||||
|
||||
/* Update top bar clock from RTC */
|
||||
ui_set_clock(s_rtc_clock->getCurrentTime());
|
||||
@@ -292,22 +287,6 @@ extern "C" void mesh_housekeeping_ui_refresh(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Read environment sensors if available */
|
||||
if (env_sensors_available()) {
|
||||
struct env_data edata;
|
||||
|
||||
if (env_sensors_read(&edata) == 0) {
|
||||
ui_set_sensor_data(
|
||||
edata.has_temperature ? (int16_t)(edata.temperature_c * 10) : 0,
|
||||
edata.has_pressure ? (uint32_t)(edata.pressure_hpa * 100) : 0,
|
||||
edata.has_humidity ? (uint16_t)(edata.humidity_pct * 10) : 0,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update offline queue message count */
|
||||
ui_set_msg_count(s_mesh->getOfflineQueueCount());
|
||||
|
||||
/* Trigger display refresh */
|
||||
ui_refresh_display();
|
||||
}
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
*/
|
||||
|
||||
#include "ui_pages.h"
|
||||
#include "ui_task.h"
|
||||
#include "display.h"
|
||||
|
||||
#include <ZephyrSensorManager.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -547,34 +550,31 @@ static void render_sensors(void)
|
||||
char buf[24];
|
||||
int y = CONTENT_Y;
|
||||
|
||||
/* Temperature */
|
||||
if (state.temperature_c10 != 0) {
|
||||
/* Lazy read: only fetch sensors when the user is actually looking at
|
||||
* this page. Render is event-driven (schedule_render only on real UI
|
||||
* events), so this never fires periodically when idle. */
|
||||
struct env_data edata;
|
||||
bool have_env = env_sensors_available() && env_sensors_read(&edata) == 0;
|
||||
|
||||
if (have_env && edata.has_temperature) {
|
||||
int16_t t10 = (int16_t)(edata.temperature_c * 10);
|
||||
snprintf(buf, sizeof(buf), "Temp: %d.%d C",
|
||||
state.temperature_c10 / 10,
|
||||
abs(state.temperature_c10 % 10));
|
||||
t10 / 10, abs(t10 % 10));
|
||||
mc_display_text(0, y, buf, false);
|
||||
y += LINE_H;
|
||||
}
|
||||
|
||||
/* Pressure */
|
||||
if (state.pressure_pa != 0) {
|
||||
if (have_env && edata.has_pressure) {
|
||||
snprintf(buf, sizeof(buf), "Press: %u hPa",
|
||||
(unsigned)(state.pressure_pa / 100));
|
||||
(unsigned)edata.pressure_hpa);
|
||||
mc_display_text(0, y, buf, false);
|
||||
y += LINE_H;
|
||||
}
|
||||
|
||||
/* Humidity */
|
||||
if (state.humidity_rh10 != 0) {
|
||||
if (have_env && edata.has_humidity) {
|
||||
uint16_t h10 = (uint16_t)(edata.humidity_pct * 10);
|
||||
snprintf(buf, sizeof(buf), "Humid: %u.%u%%",
|
||||
state.humidity_rh10 / 10, state.humidity_rh10 % 10);
|
||||
mc_display_text(0, y, buf, false);
|
||||
y += LINE_H;
|
||||
}
|
||||
|
||||
/* Light */
|
||||
if (state.light_lux != 0) {
|
||||
snprintf(buf, sizeof(buf), "Light: %u lux", state.light_lux);
|
||||
h10 / 10, h10 % 10);
|
||||
mc_display_text(0, y, buf, false);
|
||||
y += LINE_H;
|
||||
}
|
||||
@@ -718,6 +718,11 @@ struct ui_state *ui_pages_get_state(void)
|
||||
|
||||
void ui_pages_render(void)
|
||||
{
|
||||
/* Refresh battery lazily — ADC only fires when the cached reading is
|
||||
* stale (≥30 s). Render is event-driven, so during idle this never
|
||||
* runs. Telemetry / stats paths bypass the cache entirely. */
|
||||
ui_refresh_battery();
|
||||
|
||||
mc_display_clear();
|
||||
render_top_bar();
|
||||
render_page_indicator();
|
||||
|
||||
@@ -86,12 +86,6 @@ struct ui_state {
|
||||
/* LEDs page */
|
||||
bool leds_disabled; /* true = LEDs off */
|
||||
|
||||
/* Sensors page */
|
||||
int16_t temperature_c10; /* temp in 0.1°C */
|
||||
uint32_t pressure_pa; /* pressure in Pa */
|
||||
uint16_t humidity_rh10; /* humidity in 0.1% RH */
|
||||
uint16_t light_lux;
|
||||
|
||||
/* Current page */
|
||||
enum ui_page current_page;
|
||||
|
||||
|
||||
@@ -1065,12 +1065,43 @@ void ui_set_gps_data(bool has_fix, uint8_t sats,
|
||||
s->gps_alt_mm = alt_mm;
|
||||
}
|
||||
|
||||
/* Lazy battery refresh: render path calls ui_refresh_battery(); we only hit
|
||||
* the ADC if the cached value is older than UI_BATT_REFRESH_MS. Telemetry /
|
||||
* stats paths bypass this and read fresh directly via _batt_cb, so over-the-
|
||||
* air consumers are unaffected — this gate only governs the local display. */
|
||||
#define UI_BATT_REFRESH_MS 30000
|
||||
|
||||
static uint16_t (*s_batt_provider)(void);
|
||||
static uint32_t s_batt_last_read_ms;
|
||||
static bool s_batt_ever_read;
|
||||
|
||||
void ui_set_battery(uint16_t mv, uint8_t pct)
|
||||
{
|
||||
struct ui_state *s = get_state();
|
||||
|
||||
s->battery_mv = mv;
|
||||
s->battery_pct = pct;
|
||||
/* Treat any explicit setter call as a fresh read so the lazy guard
|
||||
* doesn't immediately re-fire the ADC. */
|
||||
s_batt_last_read_ms = k_uptime_get_32();
|
||||
s_batt_ever_read = true;
|
||||
}
|
||||
|
||||
void ui_set_battery_provider(uint16_t (*provider)(void))
|
||||
{
|
||||
s_batt_provider = provider;
|
||||
}
|
||||
|
||||
void ui_refresh_battery(void)
|
||||
{
|
||||
if (!s_batt_provider) {
|
||||
return;
|
||||
}
|
||||
uint32_t now = k_uptime_get_32();
|
||||
if (s_batt_ever_read && (now - s_batt_last_read_ms) < UI_BATT_REFRESH_MS) {
|
||||
return;
|
||||
}
|
||||
ui_set_battery(s_batt_provider(), 0);
|
||||
}
|
||||
|
||||
void ui_set_clock(uint32_t epoch)
|
||||
@@ -1127,17 +1158,6 @@ void ui_set_node_name(const char *name)
|
||||
#endif
|
||||
}
|
||||
|
||||
void ui_set_sensor_data(int16_t temp_c10, uint32_t pressure_pa,
|
||||
uint16_t humidity_rh10, uint16_t light_lux)
|
||||
{
|
||||
struct ui_state *s = get_state();
|
||||
|
||||
s->temperature_c10 = temp_c10;
|
||||
s->pressure_pa = pressure_pa;
|
||||
s->humidity_rh10 = humidity_rh10;
|
||||
s->light_lux = light_lux;
|
||||
}
|
||||
|
||||
void ui_set_gps_available(bool available)
|
||||
{
|
||||
struct ui_state *s = get_state();
|
||||
@@ -1204,23 +1224,3 @@ void ui_set_heartbeat_led(bool enabled)
|
||||
#endif
|
||||
}
|
||||
|
||||
void ui_refresh_display(void)
|
||||
{
|
||||
if (!ui_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ZEPHCORE_UI_DISPLAY
|
||||
/* EPD displays: skip periodic housekeeping renders.
|
||||
* Each full e-paper refresh takes ~2s and causes visible flashing.
|
||||
* All meaningful events (messages, BLE, GPS fix, button presses)
|
||||
* already trigger renders via their own ui_set_*() → schedule_render().
|
||||
* Housekeeping just updates slow-changing data (clock, contact ages)
|
||||
* which will appear on the next event-driven render. */
|
||||
if (mc_display_is_epd()) {
|
||||
return;
|
||||
}
|
||||
|
||||
schedule_render();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -116,8 +116,6 @@ void ui_clear_recent(void);
|
||||
/**
|
||||
* Update sensor data for display.
|
||||
*/
|
||||
void ui_set_sensor_data(int16_t temp_c10, uint32_t pressure_pa,
|
||||
uint16_t humidity_rh10, uint16_t light_lux);
|
||||
|
||||
/**
|
||||
* Set whether GPS hardware was detected at boot.
|
||||
@@ -164,9 +162,17 @@ void ui_set_heartbeat_led(bool enabled);
|
||||
void ui_set_offgrid_mode(bool enabled);
|
||||
|
||||
/**
|
||||
* Trigger a display refresh (for periodic updates from housekeeping).
|
||||
* Register a battery-voltage provider used by ui_refresh_battery().
|
||||
* provider() must return millivolts (0 if no battery hardware).
|
||||
*/
|
||||
void ui_refresh_display(void);
|
||||
void ui_set_battery_provider(uint16_t (*provider)(void));
|
||||
|
||||
/**
|
||||
* Lazy battery refresh: re-read the ADC only if cached value is stale.
|
||||
* Called from the page render path so the ADC fires at most once per
|
||||
* 30 s and only when the display is actually being drawn.
|
||||
*/
|
||||
void ui_refresh_battery(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -603,6 +603,7 @@ int main(void)
|
||||
companion_mesh.setWriteFrameCallback(write_frame);
|
||||
companion_mesh.setPushCallback(push_callback);
|
||||
companion_mesh.setBatteryCallback(get_battery_mv);
|
||||
ui_set_battery_provider(get_battery_mv);
|
||||
companion_mesh.setRadioReconfigureCallback(radio_reconfigure);
|
||||
companion_mesh.setPinChangeCallback([](uint32_t new_pin) {
|
||||
zephcore_ble_set_passkey(new_pin);
|
||||
|
||||
@@ -276,6 +276,11 @@ static void gps_fix_callback(double lat, double lon, int64_t utc_time)
|
||||
#ifdef ZEPHCORE_LORA
|
||||
static mesh::ZephyrBoard zephyr_board;
|
||||
|
||||
static uint16_t get_battery_mv(void)
|
||||
{
|
||||
return zephyr_board.getBattMilliVolts();
|
||||
}
|
||||
|
||||
/* Radio is constructed with no prefs pointer; main() binds it to
|
||||
* repeater_mesh._prefs via setPrefs() before repeater_mesh.begin(). */
|
||||
|
||||
@@ -364,15 +369,9 @@ static void repeater_event_loop(void)
|
||||
lora_radio.getNoiseFloor());
|
||||
}
|
||||
|
||||
/* Battery every ~60s (12 × 5s housekeeping) */
|
||||
static uint8_t batt_counter;
|
||||
if (++batt_counter >= 12) {
|
||||
batt_counter = 0;
|
||||
ui_set_battery(zephyr_board.getBattMilliVolts(), 0);
|
||||
}
|
||||
/* Battery is now refreshed lazily from ui_pages_render() with
|
||||
* a 30 s freshness guard — no periodic ADC fire here. */
|
||||
#endif
|
||||
|
||||
ui_refresh_display();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -506,6 +505,7 @@ int main(void)
|
||||
prefs->cr,
|
||||
prefs->tx_power_dbm,
|
||||
lora_radio.getNoiseFloor());
|
||||
ui_set_battery_provider(get_battery_mv);
|
||||
ui_set_battery(zephyr_board.getBattMilliVolts(), 0);
|
||||
ui_set_gps_available(gps_is_available());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user