display improvements

This commit is contained in:
liquidraver
2026-06-05 14:56:11 +02:00
parent 28a8a6999d
commit f508c14dbb
7 changed files with 156 additions and 35 deletions
+1
View File
@@ -698,6 +698,7 @@ endif()
if(CONFIG_ZEPHCORE_UI_DESIGN_BUTTON)
target_sources(app PRIVATE
helpers/ui-button/ui_task.c
helpers/ui-button/time_sync.c
)
target_include_directories(app PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/helpers/ui-button
+12
View File
@@ -620,6 +620,18 @@ config ZEPHCORE_UI_DISPLAY_AUTO_OFF_MS
Time in milliseconds before the display turns off
after the last user interaction. Set to 0 to disable.
config ZEPHCORE_UI_TIME_SOURCE_FRESH_HOURS
int "Clock source tag freshness window (hours)"
default 12
help
The top-bar clock shows a one-character source tag: G (GPS),
A (app), N (network/SNTP). If the most recent sync from one of
those sources is older than this many hours, the tag falls back
to L (local) — the RTC is free-running on its own oscillator.
Set to 0 to keep the last external source tag indefinitely
(no freshness expiry). Drift on a 50 ppm crystal is ~4 s/day,
so this is a trust/freshness signal, not an accuracy limit.
config ZEPHCORE_AUTO_SHUTDOWN_MILLIVOLTS
int "Low-battery auto-shutdown threshold (mV, 0 = disabled)"
default 3300 if SOC_SERIES_NRF52
+3
View File
@@ -5,6 +5,7 @@
#include "CommonCLI.h"
#include "battery_curve.h"
#include <helpers/time_sync.h>
#include <helpers/TxtDataHelpers.h>
#include <helpers/AdvertDataHelpers.h>
#include <adapters/board/ZephyrBoard.h>
@@ -331,6 +332,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
uint32_t curr = getRTCClock()->getCurrentTime();
if (sender_timestamp > curr) {
getRTCClock()->setCurrentTime(sender_timestamp + 1);
time_sync_report(TIME_SYNC_CLI);
uint32_t now = getRTCClock()->getCurrentTime();
time_t t = (time_t)now;
struct tm *tm = gmtime(&t);
@@ -350,6 +352,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
uint32_t curr = getRTCClock()->getCurrentTime();
if (secs > curr) {
getRTCClock()->setCurrentTime(secs);
time_sync_report(TIME_SYNC_CLI);
time_t t = (time_t)secs;
struct tm *tm = gmtime(&t);
snprintf(reply, CLI_REPLY_SIZE, "OK - clock set: %02d:%02d - %d/%d/%d UTC",
+16 -3
View File
@@ -19,10 +19,14 @@ enum time_sync_source {
TIME_SYNC_CLI,
};
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK) || \
IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_BUTTON)
void time_sync_report(enum time_sync_source src);
const char *time_sync_display_label(void);
/* Current effective time source (prefers live GPS sync). TIME_SYNC_NONE
* if the RTC has never been set. */
enum time_sync_source time_sync_get_source(void);
#else
@@ -31,7 +35,16 @@ static inline void time_sync_report(enum time_sync_source src)
ARG_UNUSED(src);
}
#endif /* CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK */
static inline enum time_sync_source time_sync_get_source(void)
{
return TIME_SYNC_NONE;
}
#endif /* CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK || _BUTTON */
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
const char *time_sync_display_label(void);
#endif
#ifdef __cplusplus
}
+42
View File
@@ -0,0 +1,42 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Button UI: track which source last set the RTC, for the top-bar clock tag.
*
* The tag reflects *freshness*: an external sync (GPS / app / network) is
* shown only while it is recent (CONFIG_ZEPHCORE_UI_TIME_SOURCE_FRESH_HOURS).
* Once it ages out — or after a reboot, before any sync — the source reads as
* local (the RTC is free-running on its own oscillator).
*/
#include <time_sync.h>
#include <zephyr/kernel.h>
static enum time_sync_source s_last = TIME_SYNC_NONE;
static int64_t s_last_uptime; /* k_uptime_get() at the last report */
void time_sync_report(enum time_sync_source src)
{
s_last = src;
s_last_uptime = k_uptime_get();
}
enum time_sync_source time_sync_get_source(void)
{
/* Local/manual (CLI) and "never synced" are both just local time. */
if (s_last == TIME_SYNC_NONE || s_last == TIME_SYNC_CLI) {
return TIME_SYNC_CLI;
}
#if CONFIG_ZEPHCORE_UI_TIME_SOURCE_FRESH_HOURS > 0
int64_t age_ms = k_uptime_get() - s_last_uptime;
int64_t window_ms =
(int64_t)CONFIG_ZEPHCORE_UI_TIME_SOURCE_FRESH_HOURS * 3600 * 1000;
if (age_ms > window_ms) {
/* External sync has gone stale — fall back to local. */
return TIME_SYNC_CLI;
}
#endif
return s_last; /* GPS / APP / WIFI, still fresh */
}
+56 -32
View File
@@ -18,6 +18,7 @@
#include "ui_task.h"
#include "display.h"
#include <time_sync.h>
#include <ZephyrSensorManager.h>
#include <zephyr/kernel.h>
@@ -123,49 +124,72 @@ static uint8_t calc_battery_pct(uint16_t mv)
/* ========== Helper: Top Bar (Node Name + Battery) ========== */
/* One-char tag for where the displayed clock was last freshly synced from
* (see time_sync_get_source() for the freshness window). Always returns a
* letter — falls back to 'L' (local) when no recent external sync. */
static char time_source_tag(enum time_sync_source src)
{
switch (src) {
case TIME_SYNC_GPS: return 'G'; /* GPS fix */
case TIME_SYNC_APP: return 'A'; /* phone/companion app */
case TIME_SYNC_WIFI: return 'N'; /* network (SNTP) */
default: return 'L'; /* local: manual/CLI or stale/none */
}
}
static void render_top_bar(void)
{
/* Node name on the left */
if (state.node_name[0]) {
/* Truncate to fit left side (leave room for battery) */
char name[16];
/* Right side: "HH:MM<S> XX%" — clock (with 1-char source tag) then
* battery, right-aligned. Built first so the node name can be clipped
* to the space that remains on the left, preventing overlap. */
char right[16] = "";
int pos = 0;
strncpy(name, state.node_name, sizeof(name) - 1);
name[sizeof(name) - 1] = '\0';
mc_display_text(0, TOP_BAR_Y, name, false);
/* 24h clock from RTC epoch (only if time has been synced).
* Before sync, getCurrentTime() returns bare uptime (~seconds),
* so check for a sane epoch (after Jan 1 2025 = 1735689600). */
if (state.rtc_epoch > 1735689600) {
uint32_t day_sec = state.rtc_epoch % 86400;
uint8_t hh = day_sec / 3600;
uint8_t mm = (day_sec % 3600) / 60;
/* Source tag: G=GPS, A=app, N=network, L=local (always set). */
char src = time_source_tag(time_sync_get_source());
pos = snprintf(right, sizeof(right), "%02u:%02u%c ", hh, mm, src);
}
/* Right side: "HH:MM XX%" — clock then battery, right-aligned */
{
char right[16] = "";
int pos = 0;
/* Battery percentage */
if (state.battery_mv > 0) {
uint8_t pct = state.battery_pct;
/* 24h clock from RTC epoch (only if time has been synced).
* Before sync, getCurrentTime() returns bare uptime (~seconds),
* so check for a sane epoch (after Jan 1 2025 = 1735689600). */
if (state.rtc_epoch > 1735689600) {
uint32_t day_sec = state.rtc_epoch % 86400;
uint8_t hh = day_sec / 3600;
uint8_t mm = (day_sec % 3600) / 60;
pos = snprintf(right, sizeof(right), "%02u:%02u ", hh, mm);
if (pct == 0) {
pct = calc_battery_pct(state.battery_mv);
}
snprintf(right + pos, sizeof(right) - pos, "%u%%", pct);
}
/* Battery percentage */
if (state.battery_mv > 0) {
uint8_t pct = state.battery_pct;
int right_x = DISP_W;
if (right[0]) {
right_x = DISP_W - ((int)strlen(right) * FONT_W);
mc_display_text(right_x, TOP_BAR_Y, right, false);
}
if (pct == 0) {
pct = calc_battery_pct(state.battery_mv);
}
snprintf(right + pos, sizeof(right) - pos, "%u%%", pct);
/* Node name on the left, clipped to the gap before the right block
* (one char-width of padding so it never touches the clock). */
if (state.node_name[0]) {
char name[16];
int max_chars = (right_x - FONT_W) / FONT_W;
if (max_chars > (int)sizeof(name) - 1) {
max_chars = sizeof(name) - 1;
}
if (right[0]) {
int x = DISP_W - ((int)strlen(right) * FONT_W);
mc_display_text(x, TOP_BAR_Y, right, false);
if (max_chars < 0) {
max_chars = 0;
}
strncpy(name, state.node_name, max_chars);
name[max_chars] = '\0';
mc_display_text(0, TOP_BAR_Y, name, false);
}
}
+26
View File
@@ -4,9 +4,11 @@
#include <time_sync.h>
#include <adapters/gps/ZephyrGPSManager.h>
#include <zephyr/kernel.h>
#include <stdio.h>
static enum time_sync_source s_last = TIME_SYNC_NONE;
static int64_t s_last_uptime; /* k_uptime_get() at the last report */
static const char *source_short_name(enum time_sync_source src)
{
@@ -22,6 +24,30 @@ static const char *source_short_name(enum time_sync_source src)
void time_sync_report(enum time_sync_source src)
{
s_last = src;
s_last_uptime = k_uptime_get();
}
/* Freshness-windowed source for the top-bar clock tag. Local/manual (CLI)
* and "never synced" both read as local; an external sync ages out after
* CONFIG_ZEPHCORE_UI_TIME_SOURCE_FRESH_HOURS. (Distinct from the System >
* Info > Time label below, which is not time-windowed.) */
enum time_sync_source time_sync_get_source(void)
{
if (s_last == TIME_SYNC_NONE || s_last == TIME_SYNC_CLI) {
return TIME_SYNC_CLI;
}
#if CONFIG_ZEPHCORE_UI_TIME_SOURCE_FRESH_HOURS > 0
int64_t age_ms = k_uptime_get() - s_last_uptime;
int64_t window_ms =
(int64_t)CONFIG_ZEPHCORE_UI_TIME_SOURCE_FRESH_HOURS * 3600 * 1000;
if (age_ms > window_ms) {
return TIME_SYNC_CLI;
}
#endif
return s_last;
}
const char *time_sync_display_label(void)