ui fixes for wifi

This commit is contained in:
liquidraver committed 2026-10-15 22:24:49 +02:00
1 parent afa7ada095
commit 319369f173
17 files changed
+487 -60

No files matched your search

+5 -3
View File
@@ -975,7 +975,9 @@ Color TFT panels (T114, T096, Wireless Tracker) are wrapped as 1bpp displays for
### 8.2 Pages (Button UI)
**Companion** (up to 12 pages): Messages, Recent, Radio, Bluetooth, Advert, GPS, Buzzer (if buzzer present), LEDs, Sensors, Offgrid, DFU, Shutdown
**Companion** (up to 14 pages): Messages, Recent, Radio, Traffic, Bluetooth, WiFi (WiFi companion builds only), Advert, GPS, Buzzer (if buzzer present), LEDs, Sensors, Offgrid, DFU, Shutdown
Connection state on these pages: "an app is connected" (`UI_EVENT_APP_CONNECTED` / `_DISCONNECTED`, any transport) gates the buzzer, the message LED flash and the "Connected" line; which transport it is comes from `ui_set_link_transports()` (`UI_LINK_BLE` / `_USB` / `_TCP`, pushed by `process_link_change()` on every link edge). The Messages badge names the transport; the Bluetooth page says "Connected" only for a BLE client. The WiFi page shows the station state, SSID, IPv4 address, TCP port and whether an app is on the socket, pushed every housekeeping tick by `companion_wifi_ui_refresh()` through `ui_set_wifi()` (redraws only on a change, e-paper included). The joystick UI shows the same WiFi details as System > Info > WiFi (`WifiScreen`) on WiFi companion builds (ThinkNode M9); elsewhere its two hooks are no-ops.
**Repeater** (3 pages): Status, Radio, Shutdown
@@ -1007,8 +1009,8 @@ Pages with a **shared** flow that only tints per-row (Recent, GPS, Sensors,
Status) stay as single functions with inline `if (mc_display_has_color())` —
that already is the "one layout, colored" ideal, and the color branch
dead-code-eliminates on mono via the constant-false `mc_display_has_color()`.
Split pages: Messages, Radio, Traffic, Bluetooth, Advert, LEDs, Offgrid, DFU,
Shutdown.
Split pages: Messages, Radio, Traffic, Bluetooth, WiFi, Advert, LEDs, Offgrid,
DFU, Shutdown.
### 8.3 Multi-Tap Input
+16 -2
View File
@@ -1,7 +1,7 @@
# ZephCore 1.18.0-zephcore
Safer off-grid mode, working buttons on four boards with a display, a GPS clock fix, a new KISS
modem role, and the changes from upstream MeshCore 1.18.0.
Safer off-grid mode, working buttons on four boards with a display, a WiFi page on the display of
WiFi companions, a GPS clock fix, a new KISS modem role, and the changes from upstream MeshCore 1.18.0.
> [!NOTE]
> A normal upgrade keeps your identity, settings, contacts and phone pairing.
@@ -31,6 +31,20 @@ More in [OFFGRID.md](../docs/OFFGRID.md). Thanks to **N8HOP** for the report.
confirmed again.
- On all four boards the confirmation is a second **hold** within 3 seconds.
## Display: WiFi page, and the right connection shown
- **New WiFi page** on WiFi companions with a one-button display (Heltec V3, V4, V4.3, Wireless
Tracker, T3S3, Station G2 and others), next to the Bluetooth page. It shows whether WiFi is off,
joining or connected, and once connected the network, IP address and port to enter in the app,
and whether the app is connected over WiFi. On the ThinkNode M9 the same details are under
**System > Info > WiFi**.
- **The home page names the connection:** "WiFi OK", "USB OK" or "BLE OK" instead of always
"BLE OK", and "BLE OFF" when Bluetooth is turned off and no app is connected.
- **The Bluetooth page says "Connected" only for a Bluetooth connection.** An app connected over
WiFi or USB used to show there too.
Thanks to **bisbille** for the report.
## GPS no longer sets the clock to an old time
Just after waking, a GPS receiver can report its last position and its own, possibly stale, clock
@@ -503,6 +503,11 @@ bool zephcore_usb_companion_is_text_session(void)
return usb_session_is_text;
}
bool zephcore_usb_companion_link_up(void)
{
return usb_link_up;
}
void zephcore_usb_companion_set_cli_line_cb(void (*cb)(const char *line))
{
s_cli_line_cb = cb;
@@ -74,6 +74,12 @@ void zephcore_usb_companion_write_text(const char *text, size_t len);
*/
bool zephcore_usb_companion_is_text_session(void);
/**
* True while a session of either kind (binary app or text CLI) is open and
* the bus is not suspended: the state behind the link callbacks' edges.
*/
bool zephcore_usb_companion_link_up(void);
#ifdef __cplusplus
}
#endif
+14
View File
@@ -14,6 +14,7 @@
#include <helpers/TxtDataHelpers.h>
#include <ZephyrWiFiStation.h>
#include <helpers/pm_sleep_guard.h>
#include "ui_task.h"
LOG_MODULE_REGISTER(zephcore_wifi_companion, CONFIG_ZEPHCORE_MAIN_LOG_LEVEL);
@@ -49,6 +50,19 @@ void companion_wifi_start(const NodePrefs &prefs)
}
}
/* Off when start() did not join (s_ssid stays empty); up once there is a
* lease, which is what the app needs to connect. */
void companion_wifi_ui_refresh(void)
{
char ip[16] = "";
uint8_t state = UI_WIFI_OFF;
if (s_ssid[0] != '\0') {
state = zc_wifi_station_ip(ip, sizeof(ip)) ? UI_WIFI_UP : UI_WIFI_JOINING;
}
ui_set_wifi(state, s_ssid, ip, CONFIG_ZEPHCORE_TCP_PORT);
}
bool companion_wifi_cli(const char *command, NodePrefs &prefs, void (*save)(void), char *reply)
{
if (memcmp(command, "set wifi.ssid ", 14) == 0) {
+4
View File
@@ -13,6 +13,10 @@
* wifi.* change applies on the next reboot. */
void companion_wifi_start(const NodePrefs &prefs);
/* Push the station's state, SSID and IP to the display (ui_set_wifi). From
* the main loop's housekeeping tick. */
void companion_wifi_ui_refresh(void);
/* Upstream's companion wifi.* commands. True when `command` was one of them
* (reply filled); save persists the prefs. */
bool companion_wifi_cli(const char *command, NodePrefs &prefs, void (*save)(void), char *reply);
+137 -16
View File
@@ -170,6 +170,9 @@ static const enum ui_page active_pages[] = {
UI_PAGE_RADIO,
UI_PAGE_TRAFFIC,
UI_PAGE_BLUETOOTH,
#ifdef CONFIG_ZEPHCORE_COMPANION_WIFI
UI_PAGE_WIFI,
#endif
UI_PAGE_ADVERT,
UI_PAGE_GPS,
#ifdef CONFIG_ZEPHCORE_UI_BUZZER
@@ -454,6 +457,40 @@ static const char *radio_state_label(void)
state.lora_radio_ready ? "RDY" : "WAIT";
}
/* The transport the app is connected on (BLE first when several are), for the
* home page. Meaningful only while state.app_connected. */
static const char *link_label(void)
{
return (state.link_transports & UI_LINK_BLE) ? "BLE" :
(state.link_transports & UI_LINK_TCP) ? "WiFi" :
(state.link_transports & UI_LINK_USB) ? "USB" : "App";
}
#if MC_DISPLAY_COLOR_PANEL
/* The home page's link badge: "<transport> OK" while an app is connected,
* else whether BLE is advertising or off. */
static const char *link_badge(char *buf, size_t len)
{
if (state.app_connected) {
snprintf(buf, len, "%s OK", link_label());
return buf;
}
return state.ble_enabled ? "BLE ADV" : "BLE OFF";
}
static uint16_t link_badge_color(void)
{
return state.app_connected ? UI_COLOR_OK :
state.ble_enabled ? UI_COLOR_WARN : UI_COLOR_DISABLED;
}
#endif
/* True when BLE itself has a client (not just some transport). */
static bool ble_link_up(void)
{
return (state.link_transports & UI_LINK_BLE) != 0;
}
#if MC_DISPLAY_COLOR_PANEL
static uint8_t clamp_activity_delta(uint32_t delta)
{
@@ -565,6 +602,7 @@ static const char *tiny_page_title(enum ui_page p)
case UI_PAGE_RADIO: return "RADIO";
case UI_PAGE_TRAFFIC: return "TRAFFIC";
case UI_PAGE_BLUETOOTH: return "BLUETOOTH";
case UI_PAGE_WIFI: return "WIFI";
case UI_PAGE_ADVERT: return "ADVERT";
case UI_PAGE_GPS: return "GPS";
case UI_PAGE_BUZZER: return "BUZZER";
@@ -601,14 +639,19 @@ static void render_tiny_title(void)
*/
static void render_messages_mono(void)
{
/* 3 centered rows: msg count, BLE status, offgrid status */
/* 3 centered rows: msg count, link status, offgrid status */
char buf[24];
if (ui_tiny()) {
snprintf(buf, sizeof(buf), "MSG:%u", state.msg_count);
draw_centered(centered_row(0, 3), buf);
draw_centered(centered_row(1, 3),
state.ble_connected ? "BLE:conn" : "BLE:adv");
if (state.app_connected) {
snprintf(buf, sizeof(buf), "%s:conn", link_label());
} else {
snprintf(buf, sizeof(buf), "BLE:%s",
state.ble_enabled ? "adv" : "off");
}
draw_centered(centered_row(1, 3), buf);
uint32_t up_s = (uint32_t)(k_uptime_get() / 1000);
@@ -621,8 +664,9 @@ static void render_messages_mono(void)
snprintf(buf, sizeof(buf), "MSG: %u", state.msg_count);
draw_centered(centered_row(0, 4), buf);
if (state.ble_connected) {
draw_centered(centered_row(1, 4), "< Connected >");
if (state.app_connected) {
snprintf(buf, sizeof(buf), "< Connected: %s >", link_label());
draw_centered(centered_row(1, 4), buf);
} else {
draw_centered(centered_row(1, 4), "Waiting for app...");
}
@@ -644,15 +688,15 @@ static void render_messages_color(void)
if (use_compact_color_home()) {
int y = CONTENT_Y;
uint16_t ble_color = state.ble_connected ? UI_COLOR_OK : UI_COLOR_WARN;
const char *ble = state.ble_connected ? "BLE OK" : "BLE ADV";
char badge[12];
const char *link = link_badge(badge, sizeof(badge));
const char *radio = radio_state_label();
uint16_t radio_color = state.lora_tx_active ? UI_COLOR_TX :
state.lora_in_rx ? UI_COLOR_RX :
state.lora_radio_ready ? UI_COLOR_OK
: UI_COLOR_WARN;
draw_badge(0, y, ble, ble_color);
draw_badge(0, y, link, link_badge_color());
draw_badge(DISP_W - color_text_width(radio) - 4, y, radio,
radio_color);
y += LINE_H + 2;
@@ -663,7 +707,7 @@ static void render_messages_color(void)
state.msg_count ? UI_COLOR_WARN : UI_COLOR_VALUE);
y += LINE_H;
if (state.ble_connected) {
if (state.app_connected) {
draw_centered_color(y, "Connected", UI_COLOR_OK);
} else {
mc_display_color_text(30, y, "Waiting for app", UI_COLOR_WARN);
@@ -685,10 +729,9 @@ static void render_messages_color(void)
}
int y = CONTENT_Y;
uint16_t ble_color = state.ble_connected ? UI_COLOR_OK : UI_COLOR_WARN;
const char *ble = state.ble_connected ? "BLE OK" : "BLE ADV";
char badge[12];
draw_badge(0, y, ble, ble_color);
draw_badge(0, y, link_badge(badge, sizeof(badge)), link_badge_color());
draw_badge(DISP_W - color_text_width(state.offgrid_enabled ? "GRID" : "LOCAL") - 4,
y, state.offgrid_enabled ? "GRID" : "LOCAL",
state.offgrid_enabled ? UI_COLOR_ACTIVE : UI_COLOR_DISABLED);
@@ -700,7 +743,7 @@ static void render_messages_color(void)
state.msg_count ? UI_COLOR_WARN : UI_COLOR_VALUE);
y += LINE_H;
if (state.ble_connected) {
if (state.app_connected) {
draw_centered_color(y, "Connected", UI_COLOR_OK);
} else {
draw_centered_color(y, "Waiting for app", UI_COLOR_WARN);
@@ -1008,7 +1051,7 @@ static void render_bluetooth_mono(void)
return;
}
if (state.ble_connected) {
if (ble_link_up()) {
draw_centered(centered_row(0, 2), "BLE: Connected");
draw_centered(centered_row(1, 2), "Press to Disable");
} else {
@@ -1022,12 +1065,12 @@ static void render_bluetooth_color(void)
{
int y = CONTENT_Y;
uint16_t color = !state.ble_enabled ? UI_COLOR_DISABLED :
state.ble_connected ? UI_COLOR_OK : UI_COLOR_WARN;
ble_link_up() ? UI_COLOR_OK : UI_COLOR_WARN;
draw_badge(0, y, "BLE", color);
mc_display_color_text(32, y,
!state.ble_enabled ? "disabled" :
state.ble_connected ? "connected" : "advertising",
ble_link_up() ? "connected" : "advertising",
color);
y += LINE_H + (FONT_H / 4);
draw_centered_color(y,
@@ -1048,6 +1091,81 @@ static void render_bluetooth(void)
render_bluetooth_mono();
}
#ifdef CONFIG_ZEPHCORE_COMPANION_WIFI
/* WiFi page: state, network, and the address the app connects to. */
static void render_wifi_mono(void)
{
char buf[24];
if (state.wifi_state == UI_WIFI_OFF) {
draw_centered(centered_row(0, 2), "WiFi: OFF");
draw_centered(centered_row(1, 2), "Set up via CLI");
return;
}
if (state.wifi_state == UI_WIFI_JOINING) {
draw_centered(centered_row(0, 2), "WiFi: Joining");
draw_centered(centered_row(1, 2), state.wifi_ssid);
return;
}
draw_centered(centered_row(0, 4), state.wifi_ssid);
draw_centered(centered_row(1, 4), state.wifi_ip);
snprintf(buf, sizeof(buf), "Port %u", state.wifi_port);
draw_centered(centered_row(2, 4), buf);
draw_centered(centered_row(3, 4),
(state.link_transports & UI_LINK_TCP) ? "App connected"
: "No app");
}
#if MC_DISPLAY_COLOR_PANEL
static void render_wifi_color(void)
{
int y = CONTENT_Y;
char buf[8];
uint16_t color = state.wifi_state == UI_WIFI_UP ? UI_COLOR_OK :
state.wifi_state == UI_WIFI_JOINING ? UI_COLOR_WARN :
UI_COLOR_DISABLED;
draw_badge(0, y, "WiFi", color);
mc_display_color_text(color_text_width("WiFi") + 8, y,
state.wifi_state == UI_WIFI_UP ? "connected" :
state.wifi_state == UI_WIFI_JOINING ? "joining" : "off",
color);
y += LINE_H + (FONT_H / 4);
if (state.wifi_state == UI_WIFI_OFF) {
draw_centered_color(y, "Set up via CLI", UI_COLOR_VALUE);
return;
}
draw_color_segments(y, "SSID ", state.wifi_ssid, UI_COLOR_VALUE);
y += LINE_H;
if (state.wifi_state == UI_WIFI_JOINING) {
return;
}
draw_color_segments(y, "IP ", state.wifi_ip, UI_COLOR_VALUE);
y += LINE_H;
snprintf(buf, sizeof(buf), "%u", state.wifi_port);
draw_color_segments(y, "Port ", buf, UI_COLOR_VALUE);
y += LINE_H;
draw_color_segments(y, "App ",
(state.link_transports & UI_LINK_TCP) ? "connected" : "none",
(state.link_transports & UI_LINK_TCP) ? UI_COLOR_OK
: UI_COLOR_DISABLED);
}
#endif /* MC_DISPLAY_COLOR_PANEL */
static void render_wifi(void)
{
#if MC_DISPLAY_COLOR_PANEL
if (mc_display_has_color()) {
render_wifi_color();
return;
}
#endif
render_wifi_mono();
}
#endif /* CONFIG_ZEPHCORE_COMPANION_WIFI */
static void render_advert_mono(void)
{
/* Check for recent "Sent!" feedback (show for 2 seconds) */
@@ -1643,6 +1761,9 @@ static const page_render_fn renderers[] = {
[UI_PAGE_RADIO] = render_radio,
[UI_PAGE_TRAFFIC] = render_traffic,
[UI_PAGE_BLUETOOTH] = render_bluetooth,
#ifdef CONFIG_ZEPHCORE_COMPANION_WIFI
[UI_PAGE_WIFI] = render_wifi,
#endif
[UI_PAGE_ADVERT] = render_advert,
[UI_PAGE_GPS] = render_gps,
[UI_PAGE_BUZZER] = render_buzzer,
+11 -1
View File
@@ -25,6 +25,7 @@ enum ui_page {
UI_PAGE_RADIO, /* LoRa params (freq, SF, BW, CR, power, noise) */
UI_PAGE_TRAFFIC, /* TX/RX packet activity graph */
UI_PAGE_BLUETOOTH, /* BLE toggle */
UI_PAGE_WIFI, /* WiFi companion state and IP (WiFi builds) */
UI_PAGE_ADVERT, /* Send broadcast advert */
UI_PAGE_GPS, /* GPS status / position */
UI_PAGE_BUZZER, /* Buzzer mute toggle */
@@ -49,7 +50,8 @@ struct ui_state {
/* Messages page */
uint16_t msg_count;
bool ble_connected;
bool app_connected; /* an app is connected, on any transport */
uint8_t link_transports; /* UI_LINK_* bits: which transports have one */
/* Recently heard */
struct {
@@ -79,6 +81,14 @@ struct ui_state {
/* Bluetooth page */
bool ble_enabled; /* true = BLE active, false = serial mode */
#ifdef CONFIG_ZEPHCORE_COMPANION_WIFI
/* WiFi page */
uint8_t wifi_state; /* enum ui_wifi_state */
uint16_t wifi_port; /* TCP port of the app socket */
char wifi_ssid[33];
char wifi_ip[16]; /* dotted IPv4, "" without a lease */
#endif
/* GPS page */
bool gps_available; /* true = GNSS hardware detected at boot */
bool gps_enabled; /* GPS hardware on/off */
+60 -12
View File
@@ -53,6 +53,7 @@
#include <zephyr/drivers/gpio.h>
#include <zephyr/input/input.h>
#include <zephyr/dt-bindings/input/input-event-codes.h>
#include <stdio.h>
#include <string.h>
#include <zephyr_poweroff.h>
@@ -195,7 +196,7 @@ static void splash_work_handler(struct k_work *work)
static void schedule_render(void);
static void schedule_render_auto(void);
static void set_ble_connected(bool connected);
static void set_app_connected(bool connected);
static void advert_defer_handler(struct k_work *work)
{
@@ -819,7 +820,7 @@ void ui_notify(enum ui_event event)
is_msg_event = true;
#ifdef CONFIG_ZEPHCORE_UI_BUZZER
/* Only buzz if no phone is connected to receive the message */
if (!get_state()->ble_connected) {
if (!get_state()->app_connected) {
buzzer_play(MELODY_MSG_CONTACT);
}
#endif
@@ -828,7 +829,7 @@ void ui_notify(enum ui_event event)
case UI_EVENT_CHANNEL_MSG:
is_msg_event = true;
#ifdef CONFIG_ZEPHCORE_UI_BUZZER
if (!get_state()->ble_connected) {
if (!get_state()->app_connected) {
buzzer_play(MELODY_MSG_CHANNEL);
}
#endif
@@ -837,7 +838,7 @@ void ui_notify(enum ui_event event)
case UI_EVENT_ROOM_MSG:
is_msg_event = true;
#ifdef CONFIG_ZEPHCORE_UI_BUZZER
if (!get_state()->ble_connected) {
if (!get_state()->app_connected) {
buzzer_play(MELODY_MSG_CHANNEL);
}
#endif
@@ -849,23 +850,23 @@ void ui_notify(enum ui_event event)
#endif
break;
case UI_EVENT_BLE_CONNECTED:
set_ble_connected(true);
case UI_EVENT_APP_CONNECTED:
set_app_connected(true);
break;
case UI_EVENT_BLE_DISCONNECTED:
set_ble_connected(false);
case UI_EVENT_APP_DISCONNECTED:
set_app_connected(false);
break;
default:
break;
}
if (is_msg_event && !get_state()->ble_connected) {
if (is_msg_event && !get_state()->app_connected) {
ui_led_flash_msg();
}
/* Wake display on non-message notifications (BLE connect/disconnect etc).
/* Wake display on non-message notifications (app connect/disconnect etc).
* Message notifications use buzzer + LED flash instead of waking the display. */
#ifdef CONFIG_ZEPHCORE_UI_DISPLAY
#ifndef ZEPHCORE_REPEATER
@@ -910,11 +911,11 @@ void ui_set_msg_count(uint16_t count)
#endif
}
static void set_ble_connected(bool connected)
static void set_app_connected(bool connected)
{
struct ui_state *s = get_state();
s->ble_connected = connected;
s->app_connected = connected;
if (ui_initialized) {
schedule_render();
@@ -1125,6 +1126,53 @@ void ui_set_ble_enabled(bool enabled)
s->ble_enabled = enabled;
}
void ui_set_link_transports(uint8_t mask)
{
struct ui_state *s = get_state();
if (s->link_transports == mask) {
return;
}
s->link_transports = mask;
if (ui_initialized) {
schedule_render();
}
}
void ui_set_wifi(uint8_t state, const char *ssid, const char *ip, uint16_t port)
{
#if IS_ENABLED(CONFIG_ZEPHCORE_COMPANION_WIFI)
struct ui_state *s = get_state();
char new_ssid[sizeof(s->wifi_ssid)];
char new_ip[sizeof(s->wifi_ip)];
snprintf(new_ssid, sizeof(new_ssid), "%s", ssid ? ssid : "");
snprintf(new_ip, sizeof(new_ip), "%s", ip ? ip : "");
if (s->wifi_state == state && s->wifi_port == port &&
strcmp(s->wifi_ssid, new_ssid) == 0 && strcmp(s->wifi_ip, new_ip) == 0) {
return;
}
s->wifi_state = state;
s->wifi_port = port;
memcpy(s->wifi_ssid, new_ssid, sizeof(new_ssid));
memcpy(s->wifi_ip, new_ip, sizeof(new_ip));
/* Repaint only a page that shows it; e-paper included, since this
* changes rarely. */
#ifdef CONFIG_ZEPHCORE_UI_DISPLAY
if (ui_initialized && ui_pages_current() == UI_PAGE_WIFI) {
schedule_render();
}
#endif
#else
ARG_UNUSED(state);
ARG_UNUSED(ssid);
ARG_UNUSED(ip);
ARG_UNUSED(port);
#endif
}
void ui_set_buzzer_mode(uint8_t mode)
{
struct ui_state *s = get_state();
@@ -287,6 +287,17 @@ public:
bool handleInput(char c) override;
};
#ifdef CONFIG_ZEPHCORE_COMPANION_WIFI
/* ===== WifiScreen: WiFi companion state and the address the app uses ===== */
class WifiScreen : public UIScreen {
JoystickUITask *_task;
public:
WifiScreen(JoystickUITask *task);
int render(JoystickDisplay &display) override;
bool handleInput(char c) override;
};
#endif
/* ===== StopwatchScreen ===== */
class StopwatchScreen : public UIScreen {
JoystickUITask *_task;
@@ -117,13 +117,13 @@ extern "C" void ui_notify(enum ui_event event)
}
switch (event) {
case UI_EVENT_BLE_CONNECTED:
s_task->setBLEConnected(true);
case UI_EVENT_APP_CONNECTED:
s_task->setAppConnected(true);
s_task->notify();
break;
case UI_EVENT_BLE_DISCONNECTED:
s_task->setBLEConnected(false);
case UI_EVENT_APP_DISCONNECTED:
s_task->setAppConnected(false);
s_task->notify();
break;
@@ -269,4 +269,24 @@ extern "C" void ui_set_gps_enabled(bool) {}
extern "C" void ui_set_gps_state(uint8_t, uint32_t, uint32_t) {}
extern "C" void ui_set_buzzer_mode(uint8_t) {}
extern "C" void ui_set_offgrid_mode(bool) {}
/* Kept for the WiFi screen (System > Info > WiFi); WiFi companion builds only.
* Both arrive on the main thread, where the UI runs. */
#ifdef CONFIG_ZEPHCORE_COMPANION_WIFI
extern "C" void ui_set_link_transports(uint8_t mask)
{
if (s_task) {
s_task->setLinkTransports(mask);
}
}
extern "C" void ui_set_wifi(uint8_t state, const char *ssid, const char *ip, uint16_t port)
{
if (s_task) {
s_task->setWifi(state, ssid, ip, port);
}
}
#else
extern "C" void ui_set_link_transports(uint8_t) {}
extern "C" void ui_set_wifi(uint8_t, const char *, const char *, uint16_t) {}
#endif
extern "C" void ui_set_msg_count(uint16_t count) {}
@@ -512,7 +512,7 @@ JoystickUITask::JoystickUITask()
_next_refresh(0), _last_blt_ms(0), _pending_render(false), _render_immediate(false),
_screen_off_ms(AUTO_OFF_MILLIS), _was_display_on(false),
_cached_batt_mv(0), _battery_display_mode(0),
_brightness(100), _wake_on_msg(true), _ble_connected(false),
_brightness(100), _wake_on_msg(true), _app_connected(false),
_ble_enabled(true), _noise_floor(-120),
_pkt_recv(0), _pkt_sent(0), _pkt_errors(0), _alert_expiry(0),
_locked(false), _lock_step(0),
@@ -596,6 +596,9 @@ void JoystickUITask::begin(BaseChatMesh *mesh, mesh::ZephyrRTCClock *rtc, NodePr
_rename_node = new RenameNodeScreen(this);
_ble_code = new BLECodeScreen(this);
_stats = new StatsScreen(this);
#ifdef CONFIG_ZEPHCORE_COMPANION_WIFI
_wifi = new WifiScreen(this);
#endif
_stopwatch = new StopwatchScreen(this);
_countdown = new CountdownScreen(this);
_snake = new SnakeScreen(this);
@@ -646,6 +649,17 @@ void JoystickUITask::gotoT9InputScreenWithPrefix(const char *prefix)
void JoystickUITask::gotoRenameNodeScreen() { setCurrScreen(_rename_node); }
void JoystickUITask::gotoBLECodeScreen() { setCurrScreen(_ble_code); }
void JoystickUITask::gotoStatsScreen() { setCurrScreen(_stats); }
#ifdef CONFIG_ZEPHCORE_COMPANION_WIFI
void JoystickUITask::gotoWifiScreen() { setCurrScreen(_wifi); }
void JoystickUITask::setWifi(uint8_t state, const char *ssid, const char *ip, uint16_t port)
{
_wifi_state = state;
_wifi_port = port;
snprintf(_wifi_ssid, sizeof(_wifi_ssid), "%s", ssid ? ssid : "");
snprintf(_wifi_ip, sizeof(_wifi_ip), "%s", ip ? ip : "");
}
#endif
void JoystickUITask::gotoStopwatchScreen() { setCurrScreen(_stopwatch); }
void JoystickUITask::gotoCountdownScreen() { setCurrScreen(_countdown); }
void JoystickUITask::gotoSnakeScreen() { setCurrScreen(_snake); }
@@ -1165,19 +1179,19 @@ void JoystickUITask::newMsg(uint8_t path_len, const char *from_name, const char
{
(void)msgcount; /* tracked by CompanionMesh */
if (_unread) {
/* When BLE phone is connected it pulls offline queue and marks read;
* keep the message in our local history but don't count as unread. */
/* A connected app (any transport) pulls the offline queue and marks it
* read; keep the message in our local history but don't count as unread. */
_unread->addPreview(path_len, from_name, text,
/*initially_read=*/_ble_connected);
/*initially_read=*/_app_connected);
}
/* Wake display if wake on msg is enabled and no BLE peer is connected.
/* Wake display if wake on msg is enabled and no app is connected.
* mc_display_on() already reschedules display.c's auto-off timer. */
if (_wake_on_msg && !_ble_connected && !_display.isOn()) {
if (_wake_on_msg && !_app_connected && !_display.isOn()) {
_display.turnOn();
}
_next_refresh = 0;
#ifdef CONFIG_ZEPHCORE_UI_BUZZER
if (!_ble_connected) {
if (!_app_connected) {
buzzer_play("MsgRcv3:d=4,o=6,b=200:32e,32g,32b,16c7");
}
#endif
@@ -1201,16 +1215,16 @@ void JoystickUITask::newChannelMsg(const char *channel_name, const char *text,
if (_unread) {
char ch_from[48];
snprintf(ch_from, sizeof(ch_from), "#%s", channel_name ? channel_name : "?");
/* BLE-connected → phone reads via offline queue, don't count as unread. */
/* App connected → it reads via the offline queue, don't count as unread. */
_unread->addPreview(path_len, ch_from, text,
/*initially_read=*/_ble_connected);
/*initially_read=*/_app_connected);
}
if (_wake_on_msg && !_ble_connected && !_display.isOn()) {
if (_wake_on_msg && !_app_connected && !_display.isOn()) {
_display.turnOn(); /* display.c reschedules auto-off internally */
}
_next_refresh = 0;
#ifdef CONFIG_ZEPHCORE_UI_BUZZER
if (!_ble_connected) {
if (!_app_connected) {
buzzer_play("kerplop:d=16,o=6,b=120:32g#,32c#");
}
#endif
@@ -74,6 +74,9 @@ public:
void gotoRenameNodeScreen();
void gotoBLECodeScreen();
void gotoStatsScreen();
#ifdef CONFIG_ZEPHCORE_COMPANION_WIFI
void gotoWifiScreen();
#endif
void gotoStopwatchScreen();
void gotoCountdownScreen();
void gotoSnakeScreen();
@@ -180,11 +183,23 @@ public:
uint32_t getPktSent() const { return _pkt_sent; }
uint32_t getPktErrors() const { return _pkt_errors; }
/* BLE status (fed from main companion loop) */
void setBLEConnected(bool connected) { _ble_connected = connected; }
bool isBLEConnected() const { return _ble_connected; }
/* An app is connected on some transport (fed from main companion loop) */
void setAppConnected(bool connected) { _app_connected = connected; }
bool isAppConnected() const { return _app_connected; }
void setBLEEnabled(bool enabled) { _ble_enabled = enabled; }
#ifdef CONFIG_ZEPHCORE_COMPANION_WIFI
/* WiFi companion state, pushed by ui_set_wifi() / ui_set_link_transports()
* on the main thread, read by WifiScreen. */
void setWifi(uint8_t state, const char *ssid, const char *ip, uint16_t port);
void setLinkTransports(uint8_t mask) { _link_transports = mask; }
uint8_t getWifiState() const { return _wifi_state; }
const char *getWifiSsid() const { return _wifi_ssid; }
const char *getWifiIp() const { return _wifi_ip; }
uint16_t getWifiPort() const { return _wifi_port; }
uint8_t getLinkTransports() const { return _link_transports; }
#endif
/* RepeaterAdmin callbacks */
void onRepeaterAdminLoginResult(const uint8_t *pub_key_prefix, bool success,
uint8_t permissions, uint32_t server_time);
@@ -233,6 +248,9 @@ private:
RenameNodeScreen *_rename_node;
BLECodeScreen *_ble_code;
StatsScreen *_stats;
#ifdef CONFIG_ZEPHCORE_COMPANION_WIFI
WifiScreen *_wifi = nullptr;
#endif
StopwatchScreen *_stopwatch;
CountdownScreen *_countdown;
SnakeScreen *_snake;
@@ -264,8 +282,15 @@ private:
int _battery_display_mode;
uint8_t _brightness;
bool _wake_on_msg;
bool _ble_connected;
bool _app_connected;
bool _ble_enabled;
#ifdef CONFIG_ZEPHCORE_COMPANION_WIFI
uint8_t _wifi_state = 0; /* enum ui_wifi_state */
uint8_t _link_transports = 0; /* UI_LINK_* bits */
uint16_t _wifi_port = 0;
char _wifi_ssid[33] = {};
char _wifi_ip[16] = {};
#endif
int16_t _noise_floor;
uint32_t _pkt_recv, _pkt_sent, _pkt_errors;
@@ -11,6 +11,7 @@
#include <helpers/time_sync.h>
#include <helpers/ui/display.h>
#include <helpers/ui/ui_mesh_actions.h>
#include <helpers/ui/ui_task.h>
#include <zephyr/kernel.h>
#include <zephyr/random/random.h>
#include <math.h>
@@ -40,7 +41,11 @@ enum SysDspItem { SYSDSP_BRIGHT=0, SYSDSP_SCROFF, SYSDSP_BATT, SYSDSP_WAKE,
#endif
SYSDSP_INPUT_ROTATE, SYSDSP_COUNT };
/* Info submenu items */
enum SysInfoItem { SYSINFO_TIME=0, SYSINFO_STATS, SYSINFO_RADIO, SYSINFO_COUNT };
enum SysInfoItem { SYSINFO_TIME=0, SYSINFO_STATS, SYSINFO_RADIO,
#ifdef CONFIG_ZEPHCORE_COMPANION_WIFI
SYSINFO_WIFI,
#endif
SYSINFO_COUNT };
/* Power submenu items */
enum SysPwrItem { SYSPWR_REBOOT=0, SYSPWR_SHUTDOWN, SYSPWR_COUNT };
@@ -112,7 +117,11 @@ int SystemScreen::render(JoystickDisplay &display)
return 500;
}
if (_mode == SYSMODE_INFO) {
static const char * const kItems[SYSINFO_COUNT] = { "Time", "System Stats", "Radio Stats" };
static const char * const kItems[SYSINFO_COUNT] = { "Time", "System Stats", "Radio Stats",
#ifdef CONFIG_ZEPHCORE_COMPANION_WIFI
"WiFi",
#endif
};
renderSubMenu(display, "Info", _selected, kItems, SYSINFO_COUNT);
return 500;
}
@@ -258,6 +267,9 @@ bool SystemScreen::handleInput(char c)
case SYSINFO_TIME: _task->gotoSystemTimeScreen(); return true;
case SYSINFO_STATS: _task->gotoStatsScreen(); return true;
case SYSINFO_RADIO: _task->gotoRadioStatsScreen(); return true;
#ifdef CONFIG_ZEPHCORE_COMPANION_WIFI
case SYSINFO_WIFI: _task->gotoWifiScreen(); return true;
#endif
default: return false;
}
}
@@ -970,6 +982,60 @@ bool StatsScreen::handleInput(char c)
return false;
}
#ifdef CONFIG_ZEPHCORE_COMPANION_WIFI
WifiScreen::WifiScreen(JoystickUITask *task)
: _task(task)
{
}
/* Same states and wording as the button UI's WiFi page; redrawn every second. */
int WifiScreen::render(JoystickDisplay &display)
{
renderScreenHeader(display, "WiFi", 0, 0);
display.setTextSize(1);
char line[48];
int y = kContentY + 2;
uint8_t state = _task->getWifiState();
if (state == UI_WIFI_OFF) {
display.setColor(JoystickDisplay::LIGHT);
display.drawTextLeftAlign(0, y, "Status: off");
y += kMenuLineH;
display.drawTextLeftAlign(0, y, "Set up via CLI");
return 1000;
}
display.setColor(state == UI_WIFI_UP ? JoystickDisplay::GREEN : JoystickDisplay::YELLOW);
display.drawTextLeftAlign(0, y, state == UI_WIFI_UP ? "Status: connected" : "Status: joining");
y += kMenuLineH;
display.setColor(JoystickDisplay::LIGHT);
snprintf(line, sizeof(line), "SSID: %s", _task->getWifiSsid());
display.drawTextLeftAlign(0, y, line);
y += kMenuLineH;
if (state != UI_WIFI_UP) {
return 1000;
}
snprintf(line, sizeof(line), "IP: %s", _task->getWifiIp());
display.drawTextLeftAlign(0, y, line);
y += kMenuLineH;
snprintf(line, sizeof(line), "Port: %u", (unsigned)_task->getWifiPort());
display.drawTextLeftAlign(0, y, line);
y += kMenuLineH;
display.drawTextLeftAlign(0, y, (_task->getLinkTransports() & UI_LINK_TCP)
? "App: connected" : "App: none");
return 1000;
}
bool WifiScreen::handleInput(char c)
{
if (c == KEY_CANCEL || c == KEY_HOME) { _task->gotoSystemScreen(); return true; }
return false;
}
#endif
TelemetryScreen::TelemetryScreen(JoystickUITask *task, mesh::RTCClock *rtc)
: _task(task), _rtc(rtc), _selected(0)
{
+11
View File
@@ -115,6 +115,17 @@ WEAK void ui_set_ble_enabled(bool enabled)
ARG_UNUSED(enabled);
}
WEAK void ui_set_link_transports(uint8_t mask)
{
ARG_UNUSED(mask);
}
WEAK void ui_set_wifi(uint8_t state, const char *ssid, const char *ip,
uint16_t port)
{
ARG_UNUSED(state); ARG_UNUSED(ssid); ARG_UNUSED(ip); ARG_UNUSED(port);
}
WEAK void ui_set_buzzer_mode(uint8_t mode)
{
ARG_UNUSED(mode);
+30 -2
View File
@@ -26,8 +26,20 @@ enum ui_event {
UI_EVENT_CHANNEL_MSG, /* New channel message received */
UI_EVENT_ROOM_MSG, /* New room message received */
UI_EVENT_ACK, /* ACK received */
UI_EVENT_BLE_CONNECTED, /* BLE peer connected */
UI_EVENT_BLE_DISCONNECTED, /* BLE peer disconnected */
UI_EVENT_APP_CONNECTED, /* an app connected, on any transport */
UI_EVENT_APP_DISCONNECTED, /* the last app session ended */
};
/* Companion transports with a client connected (ui_set_link_transports). */
#define UI_LINK_BLE (1u << 0)
#define UI_LINK_USB (1u << 1) /* wired serial: a binary app or a text-CLI session */
#define UI_LINK_TCP (1u << 2) /* the WiFi companion's app socket */
/* WiFi companion station state (ui_set_wifi). */
enum ui_wifi_state {
UI_WIFI_OFF = 0, /* wifi.enabled 0, or no network set */
UI_WIFI_JOINING, /* joining, or waiting for a DHCP lease */
UI_WIFI_UP, /* associated with an IPv4 address */
};
/**
@@ -163,6 +175,22 @@ void ui_set_gps_state(uint8_t state, uint32_t last_fix_age_s, uint32_t next_sear
*/
void ui_set_ble_enabled(bool enabled);
/**
* Which companion transports have a client connected (UI_LINK_* bits).
* Pushed on every link edge; the app-connected state itself comes from
* UI_EVENT_APP_CONNECTED / _DISCONNECTED.
*/
void ui_set_link_transports(uint8_t mask);
/**
* WiFi companion state for the WiFi page. ssid and ip are copied; either may
* be NULL or empty. Repaints only when something changed.
*
* @param state enum ui_wifi_state
* @param port TCP port the app connects to
*/
void ui_set_wifi(uint8_t state, const char *ssid, const char *ip, uint16_t port);
/**
* Set notification mode (for display page).
*/
+31 -3
View File
@@ -284,16 +284,41 @@ static void companion_session_cleanup(void)
#endif
}
/* Which transports have a client right now, for the display (UI_LINK_*). */
static uint8_t link_transports(void)
{
uint8_t mask = 0;
#if IS_ENABLED(CONFIG_BT)
if (zephcore_ble_is_connected()) {
mask |= UI_LINK_BLE;
}
#endif
#if ZEPHCORE_USB_STACK
if (zephcore_usb_companion_link_up()) {
mask |= UI_LINK_USB;
}
#endif
#if IS_ENABLED(CONFIG_ZEPHCORE_TRANSPORT_TCP)
if (tcp_companion_is_connected()) {
mask |= UI_LINK_TCP;
}
#endif
return mask;
}
/* MESH_EVENT_LINK: a transport (USB and TCP sessions included) connected or
* disconnected. */
* disconnected. The app-connected events stay transport-agnostic (they gate
* buzzer, LED and unread handling); the mask says which transport it is. */
static void process_link_change(void)
{
ui_set_link_transports(link_transports());
if (atomic_set(&link_ups, 0) > 0) {
ui_notify(UI_EVENT_BLE_CONNECTED);
ui_notify(UI_EVENT_APP_CONNECTED);
}
if (atomic_set(&link_downs, 0) > 0 && !companion_serial.isConnected()) {
companion_session_cleanup();
ui_notify(UI_EVENT_BLE_DISCONNECTED);
ui_notify(UI_EVENT_APP_DISCONNECTED);
}
}
@@ -461,6 +486,9 @@ static void mesh_event_loop(void)
#endif
mesh_housekeeping_ui_refresh();
#if IS_ENABLED(CONFIG_ZEPHCORE_COMPANION_WIFI) && defined(ZEPHCORE_LORA)
companion_wifi_ui_refresh();
#endif
if (companion_mesh_ptr) {
companion_mesh_ptr->timeSyncTick();