mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-10-23 16:42:29 +00:00
display improvements
This commit is contained in:
1 parent
28a8a6999d
commit
f508c14dbb
7 files changed
+156
-35
No files matched your search
@@ -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 */
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user