mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-10-09 11:16:39 +00:00
feat(l2): Arduino 3.x as the published env, limits sized for the PSRAM, stage 7 findings
- Wio_Tracker_L2_companion_solo_lvgl now builds on Arduino-ESP32 3.3.12 (pioarduino); the 2.0.17 build stays as _solo_lvgl_arduino2 (not published) - Per-board limits (defaults unchanged, L1 byte-for-byte the same): TRAIL_CAPACITY, WAYPOINT_CAPACITY, HIST_CH_MAX / HIST_DM_MAX, NEARBY_MAX; the L2 sets 4096 / 64 / 256 / 128 / 64 (in PSRAM with UiCore; internal heap 154 KB free after). ui-lvgl: 32 trail segments, contact list 256, rooms 32, 50 bubbles per conversation, pickers 128, WiFi scan 20 - Map trail drawing drops points under 2 px apart - "Nearby" is "Nodes"; advert options "Zero hop" / "Flood" - Roadmap stage 7: LVGL on other displays -- e-ink stays ui-new, an OLED spike after this roadmap, Cardputer as a colour candidate Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,12 @@
|
||||
// turn still gets a vertex roughly every min-delta of deviation — so CAPACITY
|
||||
// covers a much longer route than CAPACITY × min-delta would suggest.
|
||||
|
||||
// Boards with PSRAM raise it (the L2: -D TRAIL_CAPACITY=4096, 64 KB, since
|
||||
// UiCore is heap-allocated there, in PSRAM); the default stays for the nRF52.
|
||||
#ifndef TRAIL_CAPACITY
|
||||
#define TRAIL_CAPACITY 512
|
||||
#endif
|
||||
|
||||
struct TrailPoint {
|
||||
int32_t lat_1e6;
|
||||
int32_t lon_1e6;
|
||||
@@ -34,7 +40,7 @@ static const uint8_t TRAIL_FLAG_SEG_START = 0x01;
|
||||
|
||||
class TrailStore {
|
||||
public:
|
||||
static const int CAPACITY = 512;
|
||||
static const int CAPACITY = TRAIL_CAPACITY;
|
||||
// _count is serialised as uint16_t in the save header — fail the build loudly
|
||||
// if CAPACITY is ever grown past what that can hold, rather than truncating.
|
||||
static_assert(CAPACITY <= 0xFFFF, "TrailStore::CAPACITY must fit in the uint16_t save-header count");
|
||||
|
||||
@@ -16,9 +16,13 @@ struct Waypoint {
|
||||
char label[WAYPOINT_LABEL_LEN];
|
||||
};
|
||||
|
||||
#ifndef WAYPOINT_CAPACITY
|
||||
#define WAYPOINT_CAPACITY 16 // the L2 raises it (-D WAYPOINT_CAPACITY=64)
|
||||
#endif
|
||||
|
||||
class WaypointStore {
|
||||
public:
|
||||
static const int CAPACITY = 16;
|
||||
static const int CAPACITY = WAYPOINT_CAPACITY;
|
||||
|
||||
int count() const { return _count; }
|
||||
bool full() const { return _count >= CAPACITY; }
|
||||
|
||||
@@ -80,13 +80,22 @@ struct DmHistEntry {
|
||||
uint8_t path[MAX_HIST_PATH_BYTES];
|
||||
};
|
||||
|
||||
// Boards with PSRAM raise these (the L2: -D HIST_CH_MAX=256 -D HIST_DM_MAX=128,
|
||||
// ~80 KB in PSRAM with UiCore); the defaults are sized for the nRF52's heap.
|
||||
#ifndef HIST_CH_MAX
|
||||
#define HIST_CH_MAX 48
|
||||
#endif
|
||||
#ifndef HIST_DM_MAX
|
||||
#define HIST_DM_MAX 32
|
||||
#endif
|
||||
|
||||
class MessageHistory {
|
||||
public:
|
||||
// Shared ring for all channels combined; each slot carries a full MSG_TEXT_BUF,
|
||||
// so this ring dominates RAM — kept modest to leave heap headroom (see
|
||||
// DM_HIST_MAX). The DM ring is likewise bounded.
|
||||
static const int CH_HIST_MAX = 48;
|
||||
static const int DM_HIST_MAX = 32;
|
||||
static const int CH_HIST_MAX = HIST_CH_MAX;
|
||||
static const int DM_HIST_MAX = HIST_DM_MAX;
|
||||
|
||||
MessageHistory()
|
||||
: _hist_head(0), _hist_count(0), _dm_hist_head(0), _dm_hist_count(0) {
|
||||
|
||||
@@ -47,7 +47,10 @@ public:
|
||||
bool fav;
|
||||
};
|
||||
|
||||
static const int MAX_NEARBY = 32;
|
||||
#ifndef NEARBY_MAX
|
||||
#define NEARBY_MAX 32 // the L2 raises it (-D NEARBY_MAX=64)
|
||||
#endif
|
||||
static const int MAX_NEARBY = NEARBY_MAX;
|
||||
|
||||
static const char* const* filterLabels() {
|
||||
static const char* const L[F_COUNT] = { "All", "Fav", "Comp", "Rpt", "Room", "Snsr" };
|
||||
|
||||
@@ -66,7 +66,7 @@ static void onOpenAdminPick(lv_event_t* e) { (void)e; s_ui->showAdminPick(); }
|
||||
static void onAdminPickRow(lv_event_t* e) { s_ui->adminPick((int)(uintptr_t)lv_event_get_user_data(e)); }
|
||||
|
||||
namespace adminview {
|
||||
static const int PICK_MAX = 64;
|
||||
static const int PICK_MAX = 128;
|
||||
static uint8_t (*s_pick)[PUB_KEY_SIZE] = psramBuf<uint8_t[PUB_KEY_SIZE]>(PICK_MAX);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ static const char* const PIN_MAP[] = { "1", "2", "3", "\n", "4", "5", "6", "\n",
|
||||
LV_SYMBOL_BACKSPACE, "0", LV_SYMBOL_OK, "" };
|
||||
|
||||
static int s_fav_slot = -1; // slot the hold / pick popup is about
|
||||
static const int PICK_MAX = 64;
|
||||
static const int PICK_MAX = 128;
|
||||
static uint8_t (*s_pick_keys)[PUB_KEY_SIZE] = psramBuf<uint8_t[PUB_KEY_SIZE]>(PICK_MAX);
|
||||
enum : int { PICK_CONTACT = 100 }; // pick codes: channel index, or PICK_CONTACT + row
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace lvport {
|
||||
|
||||
// Network for map downloads: state of the link, and one HTTP GET at a time.
|
||||
enum NetState { NET_OFF, NET_CONNECTING, NET_UP, NET_FAILED };
|
||||
static const int WIFI_SCAN_MAX = 12;
|
||||
static const int WIFI_SCAN_MAX = 20;
|
||||
|
||||
#if defined(SEEED_WIO_TRACKER_L2)
|
||||
// LovyanGFX device (NV3031B QSPI panel + GT911 touch) from WioTrackerL2Display.
|
||||
|
||||
@@ -44,7 +44,8 @@ static double latToTileY(double lat, int z) {
|
||||
// (MK_NODE), the waypoint index (MK_WAYPOINT) or the live-share slot (MK_LIVE).
|
||||
enum : uint8_t { MK_NODE, MK_WAYPOINT, MK_LIVE };
|
||||
struct Mark { int32_t lat_e6, lon_e6; int idx; uint8_t kind; lv_obj_t* obj; lv_obj_t* dot; };
|
||||
static const int MAX_MARKS = 48; // >= MAX_NEARBY, and >= waypoints + live shares
|
||||
static const int MAX_MARKS = NearbyModel::MAX_NEARBY > WaypointStore::CAPACITY + LiveTrackStore::CAPACITY
|
||||
? NearbyModel::MAX_NEARBY : WaypointStore::CAPACITY + LiveTrackStore::CAPACITY; // Nearby map, or waypoints + live shares
|
||||
static Mark* s_marks = psramBuf<Mark>(MAX_MARKS);
|
||||
static int s_mark_count = 0;
|
||||
static int s_drag = 0; // px moved in the current press: a drag isn't a tap
|
||||
|
||||
@@ -21,7 +21,7 @@ enum : uint8_t { WP_NAV, WP_RENAME, WP_SHARE, WP_DELETE };
|
||||
// Trail: normalised Web-Mercator coords (0..1) cached when the trail is
|
||||
// re-read, so a pan only scales and offsets them. One lv_line per recorded
|
||||
// segment (a pause starts a new one); extra segments join the last.
|
||||
static const int TRAIL_SEGS = 8;
|
||||
static const int TRAIL_SEGS = 32; // pauses / saves split the trail; beyond this, segments join
|
||||
// Stored as float offsets from the first point (s_ox/s_oy): a float holds a
|
||||
// small offset exactly enough, but not a whole 0..1 coordinate at street zoom
|
||||
// (2^18 * 256 px across -- a float is off by pixels there).
|
||||
@@ -219,12 +219,23 @@ void UITask::layoutNav() {
|
||||
for (int s = 0; s < navmap::TRAIL_SEGS; s++) {
|
||||
lv_obj_t* l = navmap::s_trail[s];
|
||||
if (s >= navmap::s_segs || navmap::s_seg_len[s] < 2) { lv_obj_add_flag(l, LV_OBJ_FLAG_HIDDEN); continue; }
|
||||
int f = navmap::s_seg_first[s], len = navmap::s_seg_len[s];
|
||||
// Points closer than 2 px to the last kept one are dropped (the last
|
||||
// point always stays): zoomed out, thousands of points shrink to the few
|
||||
// hundred that show, so a long trail doesn't slow every map redraw.
|
||||
int f = navmap::s_seg_first[s], len = navmap::s_seg_len[s], k = 0;
|
||||
for (int i = f; i < f + len; i++) {
|
||||
navmap::s_pts[i].x = (lv_value_precise_t)lround((navmap::s_ox + navmap::s_nx[i]) * scale - mapview::s_left);
|
||||
navmap::s_pts[i].y = (lv_value_precise_t)lround((navmap::s_oy + navmap::s_ny[i]) * scale - mapview::s_top);
|
||||
lv_value_precise_t x = (lv_value_precise_t)lround((navmap::s_ox + navmap::s_nx[i]) * scale - mapview::s_left);
|
||||
lv_value_precise_t y = (lv_value_precise_t)lround((navmap::s_oy + navmap::s_ny[i]) * scale - mapview::s_top);
|
||||
if (k > 0 && i < f + len - 1) {
|
||||
lv_point_precise_t& last = navmap::s_pts[f + k - 1];
|
||||
if (fabsf((float)(x - last.x)) < 2 && fabsf((float)(y - last.y)) < 2) continue;
|
||||
}
|
||||
navmap::s_pts[f + k].x = x;
|
||||
navmap::s_pts[f + k].y = y;
|
||||
k++;
|
||||
}
|
||||
lv_line_set_points(l, &navmap::s_pts[f], len);
|
||||
if (k < 2) { lv_obj_add_flag(l, LV_OBJ_FLAG_HIDDEN); continue; }
|
||||
lv_line_set_points(l, &navmap::s_pts[f], k);
|
||||
lv_obj_remove_flag(l, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
|
||||
@@ -683,7 +694,7 @@ void UITask::navRenameDone(bool ok) {
|
||||
// ── Adding waypoints ──────────────────────────────────────────────────────────
|
||||
|
||||
void UITask::navAddWaypoint(int32_t lat, int32_t lon) {
|
||||
if (_core->waypoints.full()) { showToast("Waypoints full (16)"); return; }
|
||||
if (_core->waypoints.full()) { showToast(waypointsFull()); return; }
|
||||
if (!_core->waypoints.add(lat, lon, rtc_clock.getCurrentTime(), "")) return;
|
||||
int i = _core->waypoints.count() - 1;
|
||||
char t[48];
|
||||
@@ -698,7 +709,7 @@ void UITask::navMarkHere() {
|
||||
if (!ensureGps() || !_core->course.currentLocation(lat, lon)) return;
|
||||
uint16_t secs = NodePrefs::gpsAvgSecs(_prefs ? _prefs->gps_avg_idx : 0);
|
||||
if (secs == 0) { navAddWaypoint(lat, lon); return; }
|
||||
if (_core->waypoints.full()) { showToast("Waypoints full (16)"); return; }
|
||||
if (_core->waypoints.full()) { showToast(waypointsFull()); return; }
|
||||
navmap::s_avg.start(secs, lat, lon);
|
||||
navPollAveraging();
|
||||
}
|
||||
@@ -1115,7 +1126,7 @@ void UITask::navWaypointsPopup() {
|
||||
// "50.06142, 19.93721 Name" (decimal degrees; comma or space between them; the
|
||||
// name is optional) -> a new waypoint.
|
||||
void UITask::navCoordsPopup() {
|
||||
if (_core->waypoints.full()) { showToast("Waypoints full (16)"); return; }
|
||||
if (_core->waypoints.full()) { showToast(waypointsFull()); return; }
|
||||
lv_obj_t* panel = navPopupPanel("Add by coordinates", false);
|
||||
lv_obj_align(panel, LV_ALIGN_TOP_MID, 0, theme::STATUS_H + 4); // above the keyboard
|
||||
_nav_ta = textField(panel);
|
||||
@@ -1152,7 +1163,7 @@ void UITask::navCoordsDone(bool ok) {
|
||||
return; // keep the field for a fix
|
||||
}
|
||||
navClosePopup();
|
||||
if (!_core->waypoints.add(lat, lon, rtc_clock.getCurrentTime(), name)) { showToast("Waypoints full (16)"); return; }
|
||||
if (!_core->waypoints.add(lat, lon, rtc_clock.getCurrentTime(), name)) { showToast(waypointsFull()); return; }
|
||||
const Waypoint& w = _core->waypoints.at(_core->waypoints.count() - 1);
|
||||
char t[40];
|
||||
snprintf(t, sizeof(t), "Saved %s", w.label);
|
||||
|
||||
@@ -194,8 +194,8 @@ void UITask::advertPopup() {
|
||||
lv_obj_set_flex_flow(list, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_style_pad_row(list, theme::GAP, 0);
|
||||
sectionTitle(list, "SEND NOW");
|
||||
listRow(list, "Nearby only", "Direct range only", onAdvertGo, (void*)(uintptr_t)0);
|
||||
listRow(list, "Everyone", "Across the mesh", onAdvertGo, (void*)(uintptr_t)1);
|
||||
listRow(list, "Zero hop", "Direct range only", onAdvertGo, (void*)(uintptr_t)0);
|
||||
listRow(list, "Flood", "Across the mesh", onAdvertGo, (void*)(uintptr_t)1);
|
||||
schemaRows(list, settings::PG_ADVERT);
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ void UITask::advertSend(bool flood) {
|
||||
navClosePopup();
|
||||
bool ok = flood ? the_mesh.advertFlood() : the_mesh.advert();
|
||||
if (ok) notify(UIEventType::ack);
|
||||
showToast(!ok ? "Advert failed" : flood ? "Advert sent across the mesh" : "Advert sent to nodes in range");
|
||||
showToast(!ok ? "Advert failed" : flood ? "Flood advert sent" : "Zero-hop advert sent");
|
||||
}
|
||||
|
||||
// ── Settings > Bluetooth ──────────────────────────────────────────────────────
|
||||
|
||||
@@ -37,6 +37,12 @@ template <class T> static T* psramBuf(size_t n) {
|
||||
#include "../ui-core/KeyboardData.h"
|
||||
#include "Keyboard.h"
|
||||
|
||||
static const char* waypointsFull() {
|
||||
static char t[24];
|
||||
snprintf(t, sizeof(t), "Waypoints full (%d)", WaypointStore::CAPACITY);
|
||||
return t;
|
||||
}
|
||||
|
||||
// ui-lvgl skeleton (docs/development/ui-core.md, step 5): status bar, home,
|
||||
// conversation list, contact picker, conversation view with compose. Every
|
||||
// piece of state it shows comes from the UI Core; this file only draws it.
|
||||
@@ -837,7 +843,7 @@ namespace home {
|
||||
struct App { const char* icon; const char* text; lv_event_cb_t cb; bool unread; };
|
||||
static const App APPS[] = {
|
||||
{ LV_SYMBOL_ENVELOPE, "Messages", onOpenChats, true },
|
||||
{ UI_SYMBOL_USERS, "Nearby", onOpenNearby, false },
|
||||
{ UI_SYMBOL_USERS, "Nodes", onOpenNearby, false },
|
||||
{ UI_SYMBOL_MAP, "Map", onOpenNav, false },
|
||||
{ LV_SYMBOL_SETTINGS, "Settings", onOpenSettings, false },
|
||||
// Page 2: tools
|
||||
@@ -1003,7 +1009,7 @@ static void onOpenChannel(lv_event_t* e) {
|
||||
|
||||
// DM rows carry a 4-byte prefix; kept in a static table the rows point into.
|
||||
static uint8_t s_dm_rows[MessageHistory::DM_HIST_MAX][4];
|
||||
static const int CONTACT_ROWS_MAX = 64;
|
||||
static const int CONTACT_ROWS_MAX = 256; // "All" with a full contact table stays usable
|
||||
static uint8_t (*s_contact_rows)[PUB_KEY_SIZE] = psramBuf<uint8_t[PUB_KEY_SIZE]>(CONTACT_ROWS_MAX);
|
||||
|
||||
static void onOpenDMRow(lv_event_t* e) {
|
||||
@@ -1016,7 +1022,7 @@ static void onNewChat(lv_event_t* e) { (void)e; s_ui->showContacts(); }
|
||||
static void onChanRowHold(lv_event_t* e); // ChannelScreen.h
|
||||
static void onChanAdd(lv_event_t* e);
|
||||
static void onChanThreadMenu(lv_event_t* e);
|
||||
static const int ROOM_ROWS_MAX = 16;
|
||||
static const int ROOM_ROWS_MAX = 32;
|
||||
static uint8_t (*s_room_rows)[PUB_KEY_SIZE] = psramBuf<uint8_t[PUB_KEY_SIZE]>(ROOM_ROWS_MAX);
|
||||
static void onDMRowHold(lv_event_t* e); // ConversationScreen.h
|
||||
static void onRoomRow(lv_event_t* e);
|
||||
@@ -1261,7 +1267,7 @@ void UITask::showNearby() {
|
||||
}
|
||||
|
||||
void UITask::buildNearby() {
|
||||
lv_obj_t* body = newScreen("Nearby", true);
|
||||
lv_obj_t* body = newScreen("Nodes", true);
|
||||
lv_obj_set_style_pad_row(body, 4, 0);
|
||||
if (_header) {
|
||||
headerButton(_header, LV_SYMBOL_REFRESH, onNearbyScan, 4, NULL); // scan
|
||||
@@ -1675,7 +1681,7 @@ void UITask::nodeAction(uint8_t action) {
|
||||
openAdmin(e.pub_key);
|
||||
break;
|
||||
case NODE_WAYPOINT: {
|
||||
if (_core->waypoints.full()) { showToast("Waypoints full (16)"); break; }
|
||||
if (_core->waypoints.full()) { showToast(waypointsFull()); break; }
|
||||
char t[48];
|
||||
if (_core->waypoints.add(e.lat_e6, e.lon_e6, rtc_clock.getCurrentTime(), e.name[0] ? e.name : "Node")) {
|
||||
snprintf(t, sizeof(t), "Saved %s", _core->waypoints.at(_core->waypoints.count() - 1).label);
|
||||
@@ -1878,7 +1884,7 @@ uint32_t UITask::threadSignature() const {
|
||||
// Positions found in the shown messages ([WAY] / [LOC] / plain "lat,lon"),
|
||||
// for the Go / Save buttons under such a bubble.
|
||||
struct MsgLoc { int32_t lat, lon; char label[WAYPOINT_LABEL_LEN * 2]; };
|
||||
static const int THREAD_MAX_SHOWN = 30;
|
||||
static const int THREAD_MAX_SHOWN = 50; // newest bubbles built per conversation
|
||||
static MsgLoc* s_msg_locs = psramBuf<MsgLoc>(THREAD_MAX_SHOWN);
|
||||
static int s_msg_loc_n = 0;
|
||||
|
||||
@@ -2008,7 +2014,7 @@ void UITask::messageLocationAction(int idx, bool save) {
|
||||
if (idx < 0 || idx >= s_msg_loc_n) return;
|
||||
const MsgLoc& m = s_msg_locs[idx];
|
||||
if (save) {
|
||||
if (_core->waypoints.full()) { showToast("Waypoints full (16)"); return; }
|
||||
if (_core->waypoints.full()) { showToast(waypointsFull()); return; }
|
||||
if (_core->waypoints.add(m.lat, m.lon, rtc_clock.getCurrentTime(), m.label)) {
|
||||
char t[48];
|
||||
snprintf(t, sizeof(t), "Saved %s", _core->waypoints.at(_core->waypoints.count() - 1).label);
|
||||
|
||||
Reference in New Issue
Block a user