joystick UI:

- track real RTC sync source on the joystick Time screen
- gate joystick-only UI helpers behind stub headers so non-joystick builds skip the extra code without #ifdef at every call site.
This commit is contained in:
liquidraver
2026-05-22 11:38:51 +02:00
parent 322460fa78
commit 094ea6e8a3
11 changed files with 130 additions and 153 deletions
+1 -1
View File
@@ -430,7 +430,6 @@ target_sources(app PRIVATE
adapters/sensors/ZephyrEnvSensors.cpp
helpers/AdvertDataHelpers.cpp
helpers/oled_power.c
helpers/time_sync.c
)
# ========== Radio Driver Selection ==========
@@ -615,6 +614,7 @@ endif()
if(CONFIG_ZEPHCORE_UI_DISPLAY AND CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
target_sources(app PRIVATE
helpers/ui-joystick/time_sync.c
helpers/ui-joystick/joystick_ui_task.cpp
helpers/ui-joystick/joystick_ui_hooks.cpp
helpers/ui-joystick/screens/home.cpp
-21
View File
@@ -22,9 +22,7 @@
#include <ui_task.h>
#define ZEPHCORE_HAS_UI_TASK 1
#endif
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
#include <joystick_ui_hooks.h>
#endif
LOG_MODULE_REGISTER(zephcore_companion, CONFIG_ZEPHCORE_MAIN_LOG_LEVEL);
/* Protocol commands (matches Arduino companion_radio) - sorted by opcode */
@@ -697,18 +695,12 @@ ContactInfo *CompanionMesh::processAck(const uint8_t *data)
return lookupContactByPubKey(ci.id.pub_key, PUB_KEY_SIZE);
}
}
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
/* Joystick UI tracks its own pending DMs; let it match before falling
* through to connection-keep-alive ACKs. The joystick handles delivery
* indicator + BLE-mirror queueing internally — we just need the matched
* contact pointer for BaseChatMesh's return-path-retry hook. */
{
uint8_t recipient_pubkey[6];
if (ui_joystick_try_match_ack(ack_crc, recipient_pubkey)) {
return lookupContactByPubKey(recipient_pubkey, 6);
}
}
#endif
return checkConnectionsAck(data);
}
@@ -887,12 +879,10 @@ void CompanionMesh::onCommandDataRecv(const ContactInfo &contact, mesh::Packet *
markConnectionActive(contact);
queueContactMessage(contact, pkt, TXT_TYPE_CLI_DATA, sender_timestamp, nullptr, 0, text);
sendPush(PUSH_CODE_MSG_WAITING);
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
{
struct ui_joystick_cli_data cli = { text };
ui_notify_joystick_event(UI_JOYSTICK_CLI_RESPONSE, contact.id.pub_key, &cli);
}
#endif
}
void CompanionMesh::onSignedMessageRecv(const ContactInfo &contact, mesh::Packet *pkt,
@@ -1190,9 +1180,7 @@ uint8_t CompanionMesh::onContactRequest(const ContactInfo &contact, uint32_t sen
void CompanionMesh::logTx(mesh::Packet *, int)
{
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
ui_notify_packet_sent();
#endif
}
void CompanionMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data, uint8_t len)
@@ -1243,7 +1231,6 @@ void CompanionMesh::onContactResponse(const ContactInfo &contact, const uint8_t
i += 6;
}
sendPush(rsp[0], &rsp[1], i - 1);
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
{
struct ui_joystick_login_data login = {
rsp[0] == PUSH_CODE_LOGIN_SUCCESS,
@@ -1252,7 +1239,6 @@ void CompanionMesh::onContactResponse(const ContactInfo &contact, const uint8_t
};
ui_notify_joystick_event(UI_JOYSTICK_LOGIN_RESULT, contact.id.pub_key, &login);
}
#endif
return;
}
@@ -1340,10 +1326,6 @@ void CompanionMesh::onContactResponse(const ContactInfo &contact, const uint8_t
return;
}
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
/* Unmatched response: forward to joystick so it can check for a pending ping.
* RepeaterStats.last_snr sits at byte offset 42 in the struct payload (after the
* 4-byte tag prefix), so data[46]. Only valid when the response is long enough. */
{
int8_t snr_remote = INT8_MIN;
if (len >= 48) {
@@ -1359,7 +1341,6 @@ void CompanionMesh::onContactResponse(const ContactInfo &contact, const uint8_t
};
ui_notify_joystick_event(UI_JOYSTICK_REQ_RESPONSE, contact.id.pub_key, &rr);
}
#endif
}
/* Raw packet logging - sends all RX packets to app for "heard X repeats" etc */
@@ -1418,7 +1399,6 @@ void CompanionMesh::onControlDataRecv(mesh::Packet *packet)
return;
}
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
if (packet->payload_len >= 6 + PUB_KEY_SIZE &&
(packet->payload[0] & 0xF0) == CTL_TYPE_NODE_DISCOVER_RESP &&
(packet->payload[0] & 0x0F) == ADV_TYPE_REPEATER) {
@@ -1427,7 +1407,6 @@ void CompanionMesh::onControlDataRecv(mesh::Packet *packet)
};
ui_notify_joystick_event(UI_JOYSTICK_DISCOVER_RESP, &packet->payload[6], &dd);
}
#endif
uint8_t buf[MAX_FRAME_SIZE];
int i = 0;
-2
View File
@@ -11,7 +11,6 @@
#include <adapters/sensors/SimpleLPP.h>
#include <adapters/sensors/ZephyrEnvSensors.h>
#include <adapters/gps/ZephyrGPSManager.h>
#include <helpers/time_sync.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
@@ -54,7 +53,6 @@ static void uplink_time_sync_cb(uint32_t unix_ts)
{
if (s_uplink_mesh) {
s_uplink_mesh->getRTCClock()->setCurrentTime(unix_ts);
time_sync_report(TIME_SYNC_WIFI);
}
}
#endif
-2
View File
@@ -17,7 +17,6 @@
#include <zephyr/kernel.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/ring_buffer.h>
#include <helpers/time_sync.h>
#define CLI_REPLY_SIZE 256
@@ -222,7 +221,6 @@ static mesh::ZephyrRTCClock s_rtc_clock;
static void time_sync_cb(uint32_t unix_ts)
{
s_rtc_clock.setCurrentTime(unix_ts);
time_sync_report(TIME_SYNC_WIFI);
LOG_INF("RTC synced from SNTP: %u", unix_ts);
}
-3
View File
@@ -4,7 +4,6 @@
*/
#include "CommonCLI.h"
#include <helpers/time_sync.h>
#include <helpers/TxtDataHelpers.h>
#include <helpers/AdvertDataHelpers.h>
#include <adapters/board/ZephyrBoard.h>
@@ -328,7 +327,6 @@ 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);
@@ -348,7 +346,6 @@ 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",
+112
View File
@@ -0,0 +1,112 @@
/*
* ZephCore - Joystick UI Hooks (public API)
* Copyright (c) 2026 ZephCore
* SPDX-License-Identifier: Apache-2.0
*
* Mesh layer joystick UI bridge. Real implementations live in
* helpers/ui-joystick/joystick_ui_hooks.cpp when CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK=y.
* Inline no-ops below keep call sites free of #ifdef when joystick UI is off.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <zephyr/sys/util.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*ui_signal_fn)(void);
enum ui_joystick_event_type {
UI_JOYSTICK_LOGIN_RESULT = 0,
UI_JOYSTICK_CLI_RESPONSE = 1,
UI_JOYSTICK_REQ_RESPONSE = 2,
UI_JOYSTICK_DISCOVER_RESP = 3,
};
struct ui_joystick_login_data {
bool success;
uint8_t permissions;
uint32_t server_time;
};
struct ui_joystick_cli_data {
const char *text;
};
struct ui_joystick_req_response_data {
int8_t snr_local;
int8_t snr_remote;
const uint8_t *data;
uint8_t data_len;
};
struct ui_joystick_discover_data {
int8_t snr;
int8_t snr_remote;
uint8_t path_len;
};
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
void joystick_ui_hooks_register(void *task,
ui_signal_fn signal_refresh,
ui_signal_fn signal_tx);
void ui_notify_joystick_event(enum ui_joystick_event_type type,
const uint8_t *pub_key_prefix,
const void *payload);
void ui_notify_radio_stats(uint32_t pkt_recv, uint32_t pkt_sent, uint32_t pkt_errors);
void ui_signal_refresh(void);
void ui_signal_tx(void);
bool ui_joystick_try_match_ack(uint32_t ack, uint8_t out_pubkey[6]);
#else
static inline void joystick_ui_hooks_register(void *task,
ui_signal_fn signal_refresh,
ui_signal_fn signal_tx)
{
ARG_UNUSED(task);
ARG_UNUSED(signal_refresh);
ARG_UNUSED(signal_tx);
}
static inline void ui_notify_joystick_event(enum ui_joystick_event_type type,
const uint8_t *pub_key_prefix,
const void *payload)
{
ARG_UNUSED(type);
ARG_UNUSED(pub_key_prefix);
ARG_UNUSED(payload);
}
static inline void ui_notify_radio_stats(uint32_t pkt_recv, uint32_t pkt_sent,
uint32_t pkt_errors)
{
ARG_UNUSED(pkt_recv);
ARG_UNUSED(pkt_sent);
ARG_UNUSED(pkt_errors);
}
static inline void ui_signal_refresh(void) {}
static inline void ui_signal_tx(void) {}
static inline bool ui_joystick_try_match_ack(uint32_t ack, uint8_t out_pubkey[6])
{
ARG_UNUSED(ack);
ARG_UNUSED(out_pubkey);
return false;
}
#endif /* CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK */
#ifdef __cplusplus
}
#endif
+14 -4
View File
@@ -1,10 +1,12 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Track which source last set the RTC for UI display.
* Joystick UI: track which source last set the RTC (System > Info > Time).
*/
#pragma once
#include <zephyr/sys/util.h>
#ifdef __cplusplus
extern "C" {
#endif
@@ -17,12 +19,20 @@ enum time_sync_source {
TIME_SYNC_CLI,
};
/** Record a successful RTC set from @p src. */
void time_sync_report(enum time_sync_source src);
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
/** Label for System > Info > Time. GPS authority wins while gps_has_time_sync(). */
void time_sync_report(enum time_sync_source src);
const char *time_sync_display_label(void);
#else
static inline void time_sync_report(enum time_sync_source src)
{
ARG_UNUSED(src);
}
#endif /* CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK */
#ifdef __cplusplus
}
#endif
@@ -3,119 +3,8 @@
* Copyright (c) 2026 ZephCore
* SPDX-License-Identifier: Apache-2.0
*
* Bridges the ui_task.h C API to JoystickUITask, and declares the
* joystick-only extension API used by CompanionMesh when
* CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK=y.
*
* Non-joystick builds never include this header; guards in CompanionMesh.cpp
* and main_companion.cpp wrap every include and call site.
* Deprecated include path use <joystick_ui_hooks.h> (helpers/).
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Signal callback type, passed during registration so joystick_ui_hooks.cpp
* can post events to the mesh loop without depending on main_companion internals.
*/
typedef void (*ui_signal_fn)(void);
/**
* Register the JoystickUITask instance and event signal callbacks.
* Must be called (from main_companion.cpp) before the mesh event loop starts.
*
* @param task Pointer to JoystickUITask instance
* @param signal_refresh Callback to wake the joystick UI loop tick
* @param signal_tx Callback to wake the mesh TX path immediately
*/
void joystick_ui_hooks_register(void *task,
ui_signal_fn signal_refresh,
ui_signal_fn signal_tx);
/*
* Joystick UI events: extend by adding a new ui_joystick_event_type value
* and payload struct rather than adding another function.
* Covers ping, login, CLI, discovery, and future joystick features.
* Only compiled in when CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK is enabled.
*/
enum ui_joystick_event_type {
UI_JOYSTICK_LOGIN_RESULT = 0, /* login accepted/rejected */
UI_JOYSTICK_CLI_RESPONSE = 1, /* admin CLI command response */
UI_JOYSTICK_REQ_RESPONSE = 2, /* unmatched contact REQ response, joystick checks for ping */
UI_JOYSTICK_DISCOVER_RESP = 3, /* CTL_TYPE_NODE_DISCOVER_RESP received */
};
struct ui_joystick_login_data {
bool success;
uint8_t permissions;
uint32_t server_time;
};
struct ui_joystick_cli_data {
const char *text;
};
struct ui_joystick_req_response_data {
int8_t snr_local; /* SNR of reply as received by us */
int8_t snr_remote; /* SNR of our REQ as seen by repeater, INT8_MIN if unavailable */
const uint8_t *data; /* response payload after the 4 byte tag (may be NULL) */
uint8_t data_len;
};
struct ui_joystick_discover_data {
int8_t snr; /* SNR of response as received by us (FROM repeater) */
int8_t snr_remote; /* SNR of our request as received by repeater (TO repeater) */
uint8_t path_len;
};
/**
* Dispatch a joystick UI event from the mesh layer.
*
* @param type Event discriminator
* @param pub_key_prefix First 4 bytes of the contact's public key
* @param payload Pointer to the matching ui_joystick_*_data struct
*/
void ui_notify_joystick_event(enum ui_joystick_event_type type,
const uint8_t *pub_key_prefix,
const void *payload);
/**
* Update radio packet counters (Rx, Tx, errors) for the joystick stats screen.
* Not part of ui_task.h, call only when CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK=y.
*/
void ui_notify_radio_stats(uint32_t pkt_recv, uint32_t pkt_sent, uint32_t pkt_errors);
/**
* Wake the joystick UI loop immediately (before the next render timer fires),
* call after a time sensitive event (ping response, login).
*/
void ui_signal_refresh(void);
/**
* Wake the mesh TX path immediately so a packet queued by the joystick UI
* (ping, login, CLI command, message) is sent without waiting for the next
* radio event.
*/
void ui_signal_tx(void);
/**
* Try to match an incoming ACK against the joystick UI's pending-DM table.
* On match: cancels the retry timer, marks the entry delivered, fires the
* deferred BLE-app mirror with the success prefix, and writes the recipient's
* 6-byte pubkey prefix to out_pubkey (caller uses it for BaseChatMesh's
* return-path-retry housekeeping).
*
* @return true if matched (out_pubkey is filled), false otherwise
*/
bool ui_joystick_try_match_ack(uint32_t ack, uint8_t out_pubkey[6]);
#ifdef __cplusplus
}
#endif
#include "../joystick_ui_hooks.h"
@@ -2,7 +2,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include "time_sync.h"
#include <time_sync.h>
#include <adapters/gps/ZephyrGPSManager.h>
#include <stdio.h>
-4
View File
@@ -27,9 +27,7 @@ LOG_MODULE_REGISTER(zephcore_ui_actions, CONFIG_ZEPHCORE_UI_ACTIONS_LOG_LEVEL);
#include <ZephyrBLE.h>
#include <ZephyrSensorManager.h>
#include "ui_task.h"
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
#include <joystick_ui_hooks.h>
#endif
#include "ui_mesh_actions.h"
/* UI action bit flags — set from input thread, consumed by mesh event loop */
@@ -335,12 +333,10 @@ extern "C" void mesh_housekeeping_ui_refresh(void)
s_mesh->prefs.cr,
s_mesh->prefs.tx_power_dbm,
s_lora_radio->getNoiseFloor());
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
ui_notify_radio_stats(
s_lora_radio->getPacketsRecv(),
s_lora_radio->getPacketsSent(),
s_lora_radio->getPacketsRecvErrors());
#endif
/* Update GPS satellite count even without fix */
if (gps_is_enabled()) {
-2
View File
@@ -41,7 +41,6 @@ extern "C" void bt_ctlr_assert_handle(char *file, uint32_t line)
#include <app/RepeaterDataStore.h>
#include <app/RepeaterMesh.h>
#include <adapters/clock/ZephyrRTCClock.h>
#include <helpers/time_sync.h>
#include <ZephyrSensorManager.h>
/* UI subsystem (display, buttons, buzzer) */
@@ -262,7 +261,6 @@ static void gps_fix_callback(double lat, double lon, int64_t utc_time)
if (utc_time > 0) {
LOG_INF("GPS fix: RTC sync time=%lld", utc_time);
rtc_clock.setCurrentTime((uint32_t)utc_time);
time_sync_report(TIME_SYNC_GPS);
}
int lat_deg = (int)lat;