Compare commits
No files matched your search
@@ -16,19 +16,19 @@
|
||||
"role": "gui",
|
||||
"title": "Touch UI",
|
||||
"version": {
|
||||
"beta_11": {
|
||||
"notes": "New keyboard navigation — drive the whole UI from the keyboard, no touchscreen needed: the WASDZ cluster moves the on-screen selection in the actual direction you press (W up, A left, Z down, D right), S selects, Q goes back, and each bottom tab has its own hotkey (E Messages, R Contacts, T Home, U Map, I Settings) shown as a small letter on its icon. On by default; toggle it any time in Settings → Keyboard.\nEvery navigation key is hotkey-bindable — the directions, select, back, the per-tab hotkeys, and the new scroll-up / scroll-down keys (default F / C) — remap them all under Settings → Keyboard.\nThis lays the groundwork for upcoming keyboard-first devices like the Tanmatsu T-Pager that have no touchscreen, and it already gives every T-Deck a fast, reliable way to use wadamesh entirely from the keyboard.\nThe selected item now shows as clean reverse-video (a bright outline for switches) instead of a coloured border, and the highlight only appears while you're actively navigating with the keys.\nThe Home hotkey mirrors tapping the Home tab — press it again on Home to flip between the Commander screen and the app drawer.\nFixed a reboot that could occur when receiving a message, especially during advert/message floods — a use-after-free while a list rebuilt mid-scroll (#27).\nAfter a crash, the device now detects it on the next boot and offers to save the crash report so you can send it in, instead of you having to dig through Settings.\nMap options can now switch the zoom control between the slider and a +/- button pair, like older builds (#26).\nRoute replay now opens on a wide overview so you can see where the hops start and the whole path at a glance — and you can pan and zoom the map freely while it plays.\nNew \"App drawer as home\" option (in the app drawer's cog) makes the launcher your home screen instead of the Commander view, for people who prefer the app grid.\nThe in-progress Tanmatsu (ESP32-P4) port source is now part of the public tree.",
|
||||
"beta_15": {
|
||||
"notes": "Power-saving now shows as an amber battery icon in the status bar — like iPhone's Low Power Mode — instead of a separate moon icon (T-Deck).\nNew @-mention picker: type @ in the message box to pick a contact from a list and drop their name in (thanks robinshizuyo-dev, #42).\nClosing a settings item, menu or chat no longer snaps the list back to the top — it stays where you left it (thanks ZorkaTheDog, #45).\nMessages you send from the MeshCore companion app now also show up on the device, in the right conversation (thanks HonzaDandy, #46).\nTyping is more reliable on the T-Deck and Tanmatsu: tapping a text field — even one that's already highlighted — starts typing right away, and the accent pop-up no longer interrupts typing outside of chats.\nThe text cursor now only appears when a field is actually ready for input, so it's a dependable \"you can type now\" cue.\nSmoother map panning: tile rendering no longer trips the watchdog and reboots during heavy scrolling.\nKeyboard navigation is now OFF by default. If you prefer driving the UI with the trackball / arrow keys, switch it back on in Settings -> Keyboard (your choice sticks until you change it). The Tanmatsu keeps it on, since it has no touchscreen.\nIncludes everything from beta_14 and the earlier betas.",
|
||||
"files": [
|
||||
{
|
||||
"type": "flash-wipe",
|
||||
"name": "wadamesh-heltec-v4-tft-merged.bin",
|
||||
"url": "https://firmware.wadamesh.com/releases/TOUCH/beta_11/wadamesh-heltec-v4-tft-merged.bin",
|
||||
"url": "https://firmware.wadamesh.com/releases/TOUCH/beta_15/wadamesh-heltec-v4-tft-merged.bin",
|
||||
"title": "Full install (bootloader + firmware)"
|
||||
},
|
||||
{
|
||||
"type": "flash-update",
|
||||
"name": "wadamesh-heltec-v4-tft.bin",
|
||||
"url": "https://firmware.wadamesh.com/releases/TOUCH/beta_11/wadamesh-heltec-v4-tft.bin",
|
||||
"url": "https://firmware.wadamesh.com/releases/TOUCH/beta_15/wadamesh-heltec-v4-tft.bin",
|
||||
"title": "Update (app only)"
|
||||
}
|
||||
]
|
||||
@@ -48,19 +48,19 @@
|
||||
"role": "gui",
|
||||
"title": "Touch UI",
|
||||
"version": {
|
||||
"beta_11": {
|
||||
"notes": "New keyboard navigation — drive the whole UI from the keyboard, no touchscreen needed: the WASDZ cluster moves the on-screen selection in the actual direction you press (W up, A left, Z down, D right), S selects, Q goes back, and each bottom tab has its own hotkey (E Messages, R Contacts, T Home, U Map, I Settings) shown as a small letter on its icon. On by default; toggle it any time in Settings → Keyboard.\nEvery navigation key is hotkey-bindable — the directions, select, back, the per-tab hotkeys, and the new scroll-up / scroll-down keys (default F / C) — remap them all under Settings → Keyboard.\nThis lays the groundwork for upcoming keyboard-first devices like the Tanmatsu T-Pager that have no touchscreen, and it already gives every T-Deck a fast, reliable way to use wadamesh entirely from the keyboard.\nThe selected item now shows as clean reverse-video (a bright outline for switches) instead of a coloured border, and the highlight only appears while you're actively navigating with the keys.\nThe Home hotkey mirrors tapping the Home tab — press it again on Home to flip between the Commander screen and the app drawer.\nFixed a reboot that could occur when receiving a message, especially during advert/message floods — a use-after-free while a list rebuilt mid-scroll (#27).\nAfter a crash, the device now detects it on the next boot and offers to save the crash report so you can send it in, instead of you having to dig through Settings.\nMap options can now switch the zoom control between the slider and a +/- button pair, like older builds (#26).\nRoute replay now opens on a wide overview so you can see where the hops start and the whole path at a glance — and you can pan and zoom the map freely while it plays.\nNew \"App drawer as home\" option (in the app drawer's cog) makes the launcher your home screen instead of the Commander view, for people who prefer the app grid.\nThe in-progress Tanmatsu (ESP32-P4) port source is now part of the public tree.",
|
||||
"beta_15": {
|
||||
"notes": "Power-saving now shows as an amber battery icon in the status bar — like iPhone's Low Power Mode — instead of a separate moon icon (T-Deck).\nNew @-mention picker: type @ in the message box to pick a contact from a list and drop their name in (thanks robinshizuyo-dev, #42).\nClosing a settings item, menu or chat no longer snaps the list back to the top — it stays where you left it (thanks ZorkaTheDog, #45).\nMessages you send from the MeshCore companion app now also show up on the device, in the right conversation (thanks HonzaDandy, #46).\nTyping is more reliable on the T-Deck and Tanmatsu: tapping a text field — even one that's already highlighted — starts typing right away, and the accent pop-up no longer interrupts typing outside of chats.\nThe text cursor now only appears when a field is actually ready for input, so it's a dependable \"you can type now\" cue.\nSmoother map panning: tile rendering no longer trips the watchdog and reboots during heavy scrolling.\nKeyboard navigation is now OFF by default. If you prefer driving the UI with the trackball / arrow keys, switch it back on in Settings -> Keyboard (your choice sticks until you change it). The Tanmatsu keeps it on, since it has no touchscreen.\nIncludes everything from beta_14 and the earlier betas.",
|
||||
"files": [
|
||||
{
|
||||
"type": "flash-wipe",
|
||||
"name": "wadamesh-tdeck-merged.bin",
|
||||
"url": "https://firmware.wadamesh.com/releases/TOUCH/beta_11/wadamesh-tdeck-merged.bin",
|
||||
"url": "https://firmware.wadamesh.com/releases/TOUCH/beta_15/wadamesh-tdeck-merged.bin",
|
||||
"title": "Full install (bootloader + firmware)"
|
||||
},
|
||||
{
|
||||
"type": "flash-update",
|
||||
"name": "wadamesh-tdeck.bin",
|
||||
"url": "https://firmware.wadamesh.com/releases/TOUCH/beta_11/wadamesh-tdeck.bin",
|
||||
"url": "https://firmware.wadamesh.com/releases/TOUCH/beta_15/wadamesh-tdeck.bin",
|
||||
"title": "Update (app only)"
|
||||
}
|
||||
]
|
||||
|
||||
+86
-9
@@ -42,13 +42,13 @@
|
||||
font-family:'JetBrains Mono',monospace; background:#fff; transition:border-color .15s, color .15s}
|
||||
.kicker:hover{border-color:var(--teal); color:var(--ink)}
|
||||
.freeline{margin:0 auto 24px; color:var(--mut); font-size:.92rem}
|
||||
.fab{position:fixed; top:16px; z-index:45; width:44px; height:44px; border-radius:50%; display:grid; place-items:center;
|
||||
.fab-rail{position:fixed; top:16px; z-index:45; display:flex; flex-direction:column; gap:12px}
|
||||
.fab-rail.left{left:16px} .fab-rail.right{right:16px}
|
||||
.fab{width:44px; height:44px; border-radius:50%; display:grid; place-items:center; cursor:pointer;
|
||||
background:#fff; border:1px solid var(--line); color:#46525c; box-shadow:0 10px 26px -14px rgba(21,24,30,.45);
|
||||
transition:border-color .15s, color .15s, transform .15s, box-shadow .15s}
|
||||
.fab:hover{border-color:var(--teal); color:var(--teal-ink); transform:translateY(-2px); box-shadow:0 14px 30px -14px rgba(21,182,166,.5)}
|
||||
.fab svg{width:21px; height:21px}
|
||||
.fab-tg{left:16px}
|
||||
.fab-bug{right:16px}
|
||||
.brand{display:flex; align-items:center; justify-content:center; gap:clamp(12px,2.6vw,20px); flex-wrap:wrap; position:relative}
|
||||
.brand .mark{width:clamp(60px,13vw,88px); height:auto; flex:none}
|
||||
.brand .word{font-weight:700; font-size:clamp(2rem,7.5vw,3.35rem); letter-spacing:3px; color:var(--ink); line-height:1}
|
||||
@@ -150,6 +150,27 @@
|
||||
.noserial{display:none;color:#b4631a;text-align:center;margin-top:14px}
|
||||
body.no-serial .noserial{display:block}
|
||||
|
||||
/* community videos */
|
||||
.vids{display:grid; gap:18px; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); margin-top:34px}
|
||||
.vid{border:1px solid var(--line); border-radius:16px; overflow:hidden; background:var(--card); box-shadow:0 12px 30px -22px rgba(21,24,30,.5)}
|
||||
.vid-thumb{position:relative; aspect-ratio:16/9; width:100%; border:0; padding:0; cursor:pointer; display:block; background-size:cover; background-position:center}
|
||||
.vid-thumb::after{content:""; position:absolute; inset:0; background:rgba(21,24,30,.12); transition:background .15s}
|
||||
.vid-thumb:hover::after{background:rgba(21,24,30,0)}
|
||||
.vid-thumb .play{position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); width:62px; height:44px; border-radius:12px;
|
||||
background:rgba(229,57,53,.94); display:grid; place-items:center; z-index:1; transition:transform .15s, background .15s}
|
||||
.vid-thumb:hover .play{transform:translate(-50%,-50%) scale(1.07); background:#e53935}
|
||||
.vid-thumb .play svg{width:24px; height:24px; color:#fff}
|
||||
.vid-embed{aspect-ratio:16/9; width:100%; border:0; display:block; background:#000}
|
||||
.vid-cap{padding:13px 15px; font-size:.92rem; color:var(--ink); font-weight:600}
|
||||
.vid-cap span{display:block; color:var(--mut); font-size:.82rem; font-weight:400; margin-top:2px}
|
||||
|
||||
/* toast (docs placeholder etc.) */
|
||||
.toast{position:fixed; left:50%; bottom:26px; transform:translateX(-50%) translateY(12px); z-index:70;
|
||||
background:var(--ink); color:#fff; padding:11px 20px; border-radius:100px; font-size:.92rem; font-weight:600;
|
||||
box-shadow:0 16px 36px -14px rgba(21,24,30,.55); opacity:0; visibility:hidden; pointer-events:none;
|
||||
transition:opacity .2s ease, transform .2s ease}
|
||||
.toast.show{opacity:1; visibility:visible; transform:translateX(-50%) translateY(0)}
|
||||
|
||||
footer{border-top:1px solid var(--line); margin-top:48px; padding:30px 0 56px; text-align:center; color:var(--mut); font-size:.86rem}
|
||||
footer a{color:var(--teal-ink)}
|
||||
.legal-links{margin-top:12px}
|
||||
@@ -176,12 +197,26 @@
|
||||
<div id="vanta-bg"></div>
|
||||
|
||||
<!-- floating quick links -->
|
||||
<a class="fab fab-tg" href="https://t.me/+fHuhG67-RFJlNmFk" target="_blank" rel="noopener" aria-label="Join us on Telegram" title="Join us on Telegram">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M9.04 15.47 8.7 19.9c.5 0 .72-.22.98-.47l2.36-2.22 4.89 3.53c.9.5 1.53.24 1.77-.83l3.2-14.5c.3-1.33-.5-1.85-1.36-1.53L1.9 9.46c-1.3.5-1.28 1.22-.22 1.55l5.05 1.57L18.4 5.9c.55-.36 1.05-.16.64.2L9.04 15.47z"/></svg>
|
||||
</a>
|
||||
<a class="fab fab-bug" href="https://github.com/ALLFATHER-BV/wadamesh/issues" target="_blank" rel="noopener" aria-label="Report a bug on GitHub" title="Report a bug">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="8" height="14" x="8" y="6" rx="4"/><path d="m19 7-3 2"/><path d="m5 7 3 2"/><path d="m19 19-3-2"/><path d="m5 19 3-2"/><path d="M20 13h-4"/><path d="M4 13h4"/><path d="m10 4 1 2"/><path d="m14 4-1 2"/></svg>
|
||||
</a>
|
||||
<div class="fab-rail left">
|
||||
<a class="fab" href="https://t.me/+fHuhG67-RFJlNmFk" target="_blank" rel="noopener" aria-label="Join us on Telegram" title="Join us on Telegram">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M9.04 15.47 8.7 19.9c.5 0 .72-.22.98-.47l2.36-2.22 4.89 3.53c.9.5 1.53.24 1.77-.83l3.2-14.5c.3-1.33-.5-1.85-1.36-1.53L1.9 9.46c-1.3.5-1.28 1.22-.22 1.55l5.05 1.57L18.4 5.9c.55-.36 1.05-.16.64.2L9.04 15.47z"/></svg>
|
||||
</a>
|
||||
<a class="fab" href="#videos" aria-label="Community videos" title="Community videos">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
<div class="fab-rail right">
|
||||
<a class="fab" href="https://github.com/ALLFATHER-BV/wadamesh/issues" target="_blank" rel="noopener" aria-label="Report a bug on GitHub" title="Report a bug">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="8" height="14" x="8" y="6" rx="4"/><path d="m19 7-3 2"/><path d="m5 7 3 2"/><path d="m19 19-3-2"/><path d="m5 19 3-2"/><path d="M20 13h-4"/><path d="M4 13h4"/><path d="m10 4 1 2"/><path d="m14 4-1 2"/></svg>
|
||||
</a>
|
||||
<button class="fab" id="docsBtn" type="button" aria-label="Documentation" title="Documentation">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"/><path d="M14 2v5h5"/><path d="M16 13H8"/><path d="M16 17H8"/><path d="M10 9H8"/></svg>
|
||||
</button>
|
||||
<a class="fab" href="https://t413.com/p/projects/MapTileDl/" target="_blank" rel="noopener" aria-label="Map tile downloader" title="Map tile downloader">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 18l-6 3V6l6-3 6 3 6-3v15l-6 3-6-3z"/><path d="M9 3v15"/><path d="M15 6v15"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
<div class="toast" id="toast" role="status" aria-live="polite"></div>
|
||||
|
||||
<header>
|
||||
<div class="wrap">
|
||||
@@ -304,6 +339,14 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="videos">
|
||||
<div class="wrap">
|
||||
<h2 class="h2">Community videos</h2>
|
||||
<p class="p">Walkthroughs, demos and reviews from the community.</p>
|
||||
<div class="vids" id="vids"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<div class="wrap">
|
||||
Built for the mesh · open source under GPL-3.0 ·
|
||||
@@ -420,6 +463,40 @@ We use no tracking, analytics, or advertising cookies. Our CDN and security prov
|
||||
if (notes.length) document.querySelectorAll('[data-whatsnew]').forEach(b => b.hidden = false);
|
||||
}).catch(()=>{});
|
||||
|
||||
// ---- community videos — add an entry here to add a video ----
|
||||
const VIDEOS = [
|
||||
{ id: 'V19od8jbFRc', title: 'WadaMesh: The Best T-Deck Firmware So Far', by: 'KDHD Stuff' },
|
||||
// { id: '<youtube-id>', title: '<title>', by: '<channel>' },
|
||||
];
|
||||
const vidsEl = document.getElementById('vids');
|
||||
if (vidsEl) VIDEOS.forEach(v => {
|
||||
const card = document.createElement('div'); card.className = 'vid';
|
||||
const btn = document.createElement('button'); btn.type = 'button'; btn.className = 'vid-thumb';
|
||||
btn.setAttribute('aria-label', 'Play: ' + (v.title || 'community video'));
|
||||
btn.style.backgroundImage = 'url(https://i.ytimg.com/vi/' + v.id + '/hqdefault.jpg)';
|
||||
btn.innerHTML = '<span class="play"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M8 5v14l11-7z"/></svg></span>';
|
||||
btn.addEventListener('click', () => {
|
||||
const f = document.createElement('iframe'); f.className = 'vid-embed';
|
||||
f.src = 'https://www.youtube-nocookie.com/embed/' + v.id + '?autoplay=1&rel=0';
|
||||
f.title = v.title || 'Community video';
|
||||
f.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture';
|
||||
f.allowFullscreen = true; f.loading = 'lazy';
|
||||
card.replaceChild(f, btn);
|
||||
});
|
||||
card.appendChild(btn);
|
||||
if (v.title) {
|
||||
const cap = document.createElement('div'); cap.className = 'vid-cap'; cap.textContent = v.title;
|
||||
if (v.by) { const s = document.createElement('span'); s.textContent = 'by ' + v.by; cap.appendChild(s); }
|
||||
card.appendChild(cap);
|
||||
}
|
||||
vidsEl.appendChild(card);
|
||||
});
|
||||
|
||||
// ---- toast + docs placeholder ----
|
||||
function toast(msg){ const t = document.getElementById('toast'); if (!t) return; t.textContent = msg; t.classList.add('show'); clearTimeout(t._t); t._t = setTimeout(() => t.classList.remove('show'), 2400); }
|
||||
const docsBtn = document.getElementById('docsBtn');
|
||||
if (docsBtn) docsBtn.addEventListener('click', () => toast('Documentation — coming soon!'));
|
||||
|
||||
</script>
|
||||
|
||||
<!-- animated mesh backdrop — Vanta NET (3D dot-and-line network) in wadamesh teal on white.
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ board_build.partitions = partitions_tft_touch.csv
|
||||
extra_scripts = pre:scripts/inject_wifi_env.py, merge-bin.py
|
||||
|
||||
build_flags =
|
||||
-w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1
|
||||
-Wno-deprecated-declarations -Wno-unused-parameter -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1
|
||||
-D MC_VENDORED_TOUCH_APP
|
||||
; NOTE: tried -D ARDUINO_USB_MODE=1 (HW-CDC, drops unused TinyUSB MSC/DFU ≈ 12 KB internal
|
||||
; DRAM) — but on the real V4 the binary companion link regressed: small per-message frames
|
||||
@@ -221,7 +221,7 @@ board_build.partitions = variants/lilygo_tdeck/partitions_tdeck_touch.csv
|
||||
extra_scripts = pre:scripts/inject_wifi_env.py, merge-bin.py
|
||||
|
||||
build_flags =
|
||||
-w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1
|
||||
-Wno-deprecated-declarations -Wno-unused-parameter -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1
|
||||
-D MC_VENDORED_TOUCH_APP
|
||||
-D LORA_FREQ=869.618
|
||||
-D LORA_BW=62.5
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# beta_12 — GPS hotfix (one user-facing note per non-blank, non-# line; # lines are section comments)
|
||||
|
||||
# --- GPS fix (headline) ---
|
||||
Fixed GPS being slow to get a fix — or sometimes never acquiring at all — on the T-Deck. The GPS receives its data over a small serial buffer, and on the busy touch UI a heavy screen redraw could stall long enough to overflow it and drop the satellite data mid-stream, which forced the receiver to start over. The buffer is now 16x larger, so those redraws no longer cost you the fix — acquisition is back to normal (a fix in about a minute instead of several, or none).
|
||||
|
||||
# --- A note from Kaj ---
|
||||
More and more people are picking up wadamesh lately — which is genuinely awesome — and it's surfacing feature requests and bug reports fast, in big numbers.
|
||||
I can't get back to every post in every channel, but I'm on it: I'm prioritising the fixes and quality-of-life improvements, and I'll be working through them later today. Thanks for all the reports and for the patience!
|
||||
@@ -0,0 +1,29 @@
|
||||
# beta_13 — community drop (one user-facing note per non-blank, non-# line; # lines are section comments)
|
||||
|
||||
# --- A note from Kaj (headline) ---
|
||||
This one's a community release — thank you! A bunch of the features below came from contributor pull requests, and the rest are the keyboard, typing and quality-of-life fixes I promised after the last drop. Huge thanks to everyone sending PRs, opening issues and reporting bugs: Yazutsu, tun0, Vybo and samuelcoustet all have their work in this build. Keep it coming.
|
||||
|
||||
# --- Typing & keyboard navigation (T-Deck + Tanmatsu) ---
|
||||
Open a chat or channel and the cursor is ready — you can type straight away. Every other text field now waits for Enter before it captures the keyboard, so the letter-navigation keys keep working until you actually want to type.
|
||||
Pressing Enter (or Esc / the back button) on an empty message box drops the cursor and re-selects the field, so you can navigate away with the buttons again.
|
||||
The menu-bar letter shortcuts no longer jump you to another page while you're typing in a field.
|
||||
New "Show menu-bar letters" setting under Settings -> Keyboard (off by default) — turn it on to see the tab hotkey letters on the bar.
|
||||
The accent pop-up no longer grabs the keyboard when it appears, so you can keep typing right through it.
|
||||
|
||||
# --- Composer ---
|
||||
New dedicated "Special characters" picker for symbols that aren't on the keyboard (% $ @ # & =, currency, maths, accents) — separate from the emoji picker.
|
||||
Long-press one of your sent messages to Resend it.
|
||||
|
||||
# --- Clock ---
|
||||
The 12-hour clock setting now applies everywhere — the top bar, the chat bubbles and the lock screen — not just the chat list.
|
||||
|
||||
# --- From contributors ---
|
||||
Room-server posts now show the actual author's name on each message instead of the room's own name. (Yazutsu, #40)
|
||||
New "Direct (0-hop)" filter for the contact list and the map — show only nodes you're hearing directly, with no repeater hops in between. (tun0, #39)
|
||||
Beer emoji in colour, tap the unread badge in the status bar to jump to your Chats, a new "notify on new contact" toggle, and Czech accents in the accent pop-up. (Vybo, #22)
|
||||
Optional trackball "edge scroll" — push the cursor past a screen edge to scroll the list underneath it (off by default, under Settings -> Keyboard). (tun0, #38)
|
||||
TCP now restarts immediately after you turn it off and on again, the OTA action is hidden when there's no update slot to use, and a TCP-toggle alert glitch is fixed. (samuelcoustet, #29)
|
||||
|
||||
# --- Fixes ---
|
||||
Heltec V4: removed the after-crash report prompt — that board has no SD card or reachable storage to save the report to, so it was a dead end. (The T-Deck still offers it.)
|
||||
When both Wi-Fi and GPS are off, the clock now sets itself from the timestamps on messages received over the mesh.
|
||||
@@ -0,0 +1,7 @@
|
||||
# beta_14 — hotfix (one user-facing note per non-blank, non-# line; # lines are section comments)
|
||||
|
||||
# --- Fix (headline) ---
|
||||
Fixed a bug introduced in beta_13: if you had turned keyboard navigation OFF (Settings -> Keyboard), the physical keyboard stopped typing into text fields entirely on the T-Deck and Tanmatsu. Typing now works again with keyboard navigation off; with it on, nothing changed. (The Heltec V4 was never affected.)
|
||||
|
||||
# --- Also in this release ---
|
||||
Includes everything from beta_13: room-server posts show the author's name, the Direct (0-hop) contact + map filter, beer emoji with a status-bar unread tap and a new-contact toast toggle and Czech accents, optional trackball edge-scroll, the dedicated special-characters picker, the 12-hour clock applied everywhere, and the keyboard navigation + typing improvements.
|
||||
@@ -0,0 +1,18 @@
|
||||
# beta_15 (one user-facing note per non-blank, non-# line; # lines are section comments)
|
||||
|
||||
# --- New ---
|
||||
Power-saving now shows as an amber battery icon in the status bar — like iPhone's Low Power Mode — instead of a separate moon icon (T-Deck).
|
||||
New @-mention picker: type @ in the message box to pick a contact from a list and drop their name in (thanks robinshizuyo-dev, #42).
|
||||
|
||||
# --- Fixed ---
|
||||
Closing a settings item, menu or chat no longer snaps the list back to the top — it stays where you left it (thanks ZorkaTheDog, #45).
|
||||
Messages you send from the MeshCore companion app now also show up on the device, in the right conversation (thanks HonzaDandy, #46).
|
||||
Typing is more reliable on the T-Deck and Tanmatsu: tapping a text field — even one that's already highlighted — starts typing right away, and the accent pop-up no longer interrupts typing outside of chats.
|
||||
The text cursor now only appears when a field is actually ready for input, so it's a dependable "you can type now" cue.
|
||||
Smoother map panning: tile rendering no longer trips the watchdog and reboots during heavy scrolling.
|
||||
|
||||
# --- Changed ---
|
||||
Keyboard navigation is now OFF by default. If you prefer driving the UI with the trackball / arrow keys, switch it back on in Settings -> Keyboard (your choice sticks until you change it). The Tanmatsu keeps it on, since it has no touchscreen.
|
||||
|
||||
# --- Also in this release ---
|
||||
Includes everything from beta_14 and the earlier betas.
|
||||
@@ -5,7 +5,9 @@
|
||||
# - manifest-heltec-v4-tft.json install dialog, so it must equal the tag)
|
||||
#
|
||||
# These live next to the rolling bins at firmware.wadamesh.com/latest/, so the
|
||||
# flasher always reflects the current release without a site redeploy.
|
||||
# flasher always reflects the current release without a site redeploy. The
|
||||
# manifests themselves point at the IMMUTABLE per-tag bins (see below) to dodge
|
||||
# the latest/*.bin 4h edge cache.
|
||||
#
|
||||
# Usage: gen-flasher-meta.py <tag> <outdir> [notes_file]
|
||||
# notes_file: one note per non-empty line (plain text); optional.
|
||||
@@ -35,7 +37,13 @@ for fn, (name, binf) in BOARDS.items():
|
||||
"new_install_prompt_erase": True,
|
||||
"builds": [{
|
||||
"chipFamily": "ESP32-S3",
|
||||
"parts": [{"path": f"https://firmware.wadamesh.com/latest/{binf}", "offset": 0}],
|
||||
# Point at the IMMUTABLE per-tag bin, NOT latest/. The latest/*.bin URLs
|
||||
# are stable filenames cached 4h (max-age=14400) and overwritten in place,
|
||||
# so for up to 4h after a release the flasher could hand out the PREVIOUS
|
||||
# beta's bytes while version.json already advertised the new tag. The
|
||||
# per-tag path is never overwritten, so its (24h) cache is always correct.
|
||||
# The manifest itself is max-age=300, so this new path propagates in <=5min.
|
||||
"parts": [{"path": f"https://firmware.wadamesh.com/releases/TOUCH/{tag}/{binf}", "offset": 0}],
|
||||
}],
|
||||
}
|
||||
with open(os.path.join(outdir, fn), "w") as f:
|
||||
|
||||
@@ -72,6 +72,28 @@ public:
|
||||
(void)is_flood; (void)snr_q4; (void)rssi;
|
||||
newMsgFromPub(path_len, from_pub, from_name, text, msgcount);
|
||||
}
|
||||
/** Room-server post. Like newMsgFromPubWithMeta, but from_pub/from_name
|
||||
* identify the ROOM (the chat thread) while `author_name` is the resolved
|
||||
* display name of the original poster — a room signed message attributes
|
||||
* the author only by a pubkey prefix, never in the text body. Default
|
||||
* ignores the author and behaves like a normal message, so non-touch UIs
|
||||
* are unaffected. */
|
||||
virtual void newRoomMsgFromPubWithMeta(uint8_t path_len, bool is_flood,
|
||||
const uint8_t* from_pub, const char* from_name,
|
||||
const char* author_name,
|
||||
const char* text, int msgcount,
|
||||
int8_t snr_q4, int8_t rssi) {
|
||||
(void)author_name;
|
||||
newMsgFromPubWithMeta(path_len, is_flood, from_pub, from_name, text, msgcount, snr_q4, rssi);
|
||||
}
|
||||
/** A message the COMPANION APP sent on our behalf (CMD_SEND_TXT_MSG). The wire protocol
|
||||
* deliberately doesn't echo sent messages back to clients, so the on-device UI is otherwise
|
||||
* the only consumer that never sees app-originated sends — this lets it mirror one as a local
|
||||
* outgoing bubble so it shows on-device too (issue #46). Default no-op for non-touch UIs. */
|
||||
virtual void appSentMsgToContact(const uint8_t* to_pub, const char* to_name, const char* text,
|
||||
uint32_t ack_hash) {
|
||||
(void)to_pub; (void)to_name; (void)text; (void)ack_hash;
|
||||
}
|
||||
virtual void notify(UIEventType t = UIEventType::none) = 0;
|
||||
virtual void appendDiag(const char* message) { (void)message; }
|
||||
/** Notify UI of an advert reception. `is_new=true` means the contact is NOT in
|
||||
|
||||
+55
-3
@@ -496,6 +496,11 @@ bool MyMesh::handleMeshcomodCommand(const char* text, int text_len) {
|
||||
s_meshcomod_pending_until_ms = 0;
|
||||
pushMeshcomodReply("cancelled");
|
||||
return true;
|
||||
} else {
|
||||
// Any other command while a confirmation is pending: block it so the
|
||||
// new command cannot silently overwrite the pending action.
|
||||
pushMeshcomodReply("pending confirmation — reply 'ok' to confirm or 'cancel' to abort");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1833,6 +1838,15 @@ ContactInfo* MyMesh::processAck(const uint8_t *data) {
|
||||
|
||||
void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packet *pkt,
|
||||
uint32_t sender_timestamp, const uint8_t *extra, int extra_len, const char *text) {
|
||||
// Clock bootstrap from a peer's send-time when we have no real clock of our own (Wi-Fi
|
||||
// and GPS off, or not yet synced). Reliable by construction: adopt ONLY a sane epoch
|
||||
// (~2023..2033, so the 1902/0 garbage and absurd-future values are ignored), and ONLY
|
||||
// while our own clock still reads unset — NTP/GPS override it the instant they sync, and
|
||||
// a clock that already reads real is never moved by the mesh.
|
||||
if (sender_timestamp > 1700000000UL && sender_timestamp < 2000000000UL &&
|
||||
getRTCClock()->getCurrentTime() < 1700000000UL) {
|
||||
getRTCClock()->setCurrentTime(sender_timestamp);
|
||||
}
|
||||
int i = 0;
|
||||
out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV_V3;
|
||||
out_frame[i++] = (int8_t)(pkt->getSNR() * 4);
|
||||
@@ -1875,7 +1889,14 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe
|
||||
* serial was disconnected, which meant after a channel message arrived
|
||||
* over TCP/BLE g_last_event stayed at `channelMessage` and the next DM
|
||||
* was routed into the channel thread (or vice versa). */
|
||||
_ui->notify(UIEventType::contactMessage);
|
||||
// A TXT_TYPE_SIGNED_PLAIN with a 4-byte sender_prefix is a room-server
|
||||
// post: `from` is the room (the thread) and the author is identified only
|
||||
// by the prefix — the text is the bare body. Plain DMs/channel msgs are
|
||||
// unchanged.
|
||||
const bool is_room_post =
|
||||
(txt_type == TXT_TYPE_SIGNED_PLAIN) && extra && extra_len >= 4;
|
||||
_ui->notify(is_room_post ? UIEventType::roomMessage
|
||||
: UIEventType::contactMessage);
|
||||
// Pass RX metadata so the touch UI can surface it via the bubble's
|
||||
// long-press Info sheet. SNR comes off the packet itself (most accurate
|
||||
// per-message); RSSI is the radio's last-RSSI, which is current since
|
||||
@@ -1885,8 +1906,29 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe
|
||||
const bool is_flood = pkt->isRouteFlood();
|
||||
uiStashRxMeta(pkt); // capture route + scope for the per-message Info popup
|
||||
_last_sender_ts = sender_timestamp; // embedded send-time -> UI bubble ts (room history replay)
|
||||
_ui->newMsgFromPubWithMeta(path_len, is_flood, from.id.pub_key, from.name,
|
||||
text, history_count, snr_q4, rssi);
|
||||
if (is_room_post) {
|
||||
// Resolve the post's author from the signed message's sender_prefix.
|
||||
// Prefer a saved contact's name; fall back to our own node name for
|
||||
// posts we authored (replayed during sync), else a short hex of the
|
||||
// prefix so the bubble never just shows the room's own name.
|
||||
char author_buf[16];
|
||||
const char* author_name;
|
||||
ContactInfo* author = lookupContactByPubKey(extra, 4);
|
||||
if (author && author->name[0]) {
|
||||
author_name = author->name;
|
||||
} else if (memcmp(extra, self_id.pub_key, 4) == 0) {
|
||||
author_name = _prefs.node_name;
|
||||
} else {
|
||||
mesh::Utils::toHex(author_buf, (uint8_t*)extra, 4);
|
||||
author_name = author_buf;
|
||||
}
|
||||
_ui->newRoomMsgFromPubWithMeta(path_len, is_flood, from.id.pub_key,
|
||||
from.name, author_name, text,
|
||||
history_count, snr_q4, rssi);
|
||||
} else {
|
||||
_ui->newMsgFromPubWithMeta(path_len, is_flood, from.id.pub_key, from.name,
|
||||
text, history_count, snr_q4, rssi);
|
||||
}
|
||||
}
|
||||
// CLI command replies don't belong in the chat thread but the touch UI
|
||||
// *does* want them — they're the response to whatever was typed into the
|
||||
@@ -1981,6 +2023,12 @@ void MyMesh::onSignedMessageRecv(const ContactInfo &from, mesh::Packet *pkt, uin
|
||||
|
||||
void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp,
|
||||
const char *text) {
|
||||
// Clock bootstrap from a channel peer's send-time (same sane-window + unset-only guard
|
||||
// as queueMessage above) so a Wi-Fi/GPS-off node can still get time off public channels.
|
||||
if (timestamp > 1700000000UL && timestamp < 2000000000UL &&
|
||||
getRTCClock()->getCurrentTime() < 1700000000UL) {
|
||||
getRTCClock()->setCurrentTime(timestamp);
|
||||
}
|
||||
int i = 0;
|
||||
out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV_V3;
|
||||
out_frame[i++] = (int8_t)(pkt->getSNR() * 4);
|
||||
@@ -2802,6 +2850,10 @@ void MyMesh::handleCmdFrame(size_t len) {
|
||||
memcpy(&out_frame[2], &expected_ack, 4);
|
||||
memcpy(&out_frame[6], &est_timeout, 4);
|
||||
_serial->writeFrame(out_frame, 10);
|
||||
// Mirror the app-sent DM into the on-device touch UI so it shows there too (#46). First
|
||||
// handling only — a transport/client retry (skip_radio_send) was already mirrored.
|
||||
if (_ui && !skip_radio_send && recipient)
|
||||
_ui->appSentMsgToContact(recipient->id.pub_key, recipient->name, text, expected_ack);
|
||||
|
||||
// Do not synthesize a private-message "recv" frame from self. That frame has no recipient
|
||||
// context and can be rendered by clients as a chat with self.
|
||||
|
||||
@@ -54,6 +54,9 @@ void MultiTransportCompanionInterface::stopTcpServer() {
|
||||
|
||||
void MultiTransportCompanionInterface::enableTcp() {
|
||||
_tcp_enabled = true;
|
||||
// Restart immediately: don't wait for the next main-loop tick, and don't
|
||||
// require wifi_started to be true (TCP itself doesn't need an IP address).
|
||||
startTcpServer(false);
|
||||
}
|
||||
|
||||
void MultiTransportCompanionInterface::disableTcp() {
|
||||
|
||||
@@ -35,7 +35,7 @@ static bool s_begun = false;
|
||||
// short read (→ treat as absent → defaults); `ver` lets later builds add fields.
|
||||
static const char* KEY_CFG = "cfg";
|
||||
static const uint16_t TOUCH_CFG_MAGIC = 0x5743; // 'WC' (WadaCfg)
|
||||
static const uint8_t TOUCH_CFG_VER = 16; // v2 sig_probe/poll; v3 tz_zone; v4 hide_node_name; v5 map_night/map_zoom; v6 map text/marker visibility; v7 app_grid_large; v8 ui_scale; v9 tb_keypad; v10 sleep_idle; v11 nav_keys; v12 map_zoom_buttons; v13 nav_dir_keys; v14 home_is_drawer; v15 kbd_nav default ON (one-time migrate); v16 nav_scroll_keys
|
||||
static const uint8_t TOUCH_CFG_VER = 18; // v2 sig_probe/poll; v3 tz_zone; v4 hide_node_name; v5 map_night/map_zoom; v6 map text/marker visibility; v7 app_grid_large; v8 ui_scale; v9 tb_keypad; v10 sleep_idle; v11 nav_keys; v12 map_zoom_buttons; v13 nav_dir_keys; v14 home_is_drawer; v15 kbd_nav default ON (one-time migrate); v16 nav_scroll_keys; v17 notify_new_contact; v18 kbd_nav OFF by default (reverses v15; T-Deck/V4 only, Tanmatsu stays on)
|
||||
|
||||
// Defaults (kept identical to the historical per-key defaults).
|
||||
static const uint16_t DEFAULT_SCREEN_TIMEOUT_S = 20;
|
||||
@@ -88,6 +88,7 @@ struct __attribute__((packed)) TouchCfg {
|
||||
uint8_t nav_dir_keys[6]; // keyboard-nav control keys (ASCII): up,down,left,right,select,back — v13 (trailing)
|
||||
uint8_t home_is_drawer; // Home tab defaults to the app drawer (1) vs the Commander screen (0, default) — v14 (trailing)
|
||||
uint8_t nav_scroll_keys[2]; // keyboard-nav scroll keys (ASCII): scroll-up, scroll-down — v16 (trailing)
|
||||
uint8_t notify_new_contact;// toast/chip when a contact is auto-discovered (bool) — v17 (trailing)
|
||||
};
|
||||
|
||||
static TouchCfg s_cfg;
|
||||
@@ -145,13 +146,26 @@ static void cfgSetDefaults(TouchCfg& c) {
|
||||
c.map_show_contacts = 1;
|
||||
c.app_grid_large = 0; // default: compact app grid (T-Deck 4 cols / V4 3 cols)
|
||||
c.ui_scale = 0; // default: 100% UI scale (native resolution)
|
||||
c.kbd_nav = 1; // default: keyboard navigation ON (v15)
|
||||
#if defined(HAS_TANMATSU)
|
||||
c.kbd_nav = 1; // Tanmatsu: no touchscreen — keyboard nav is the only input, always on
|
||||
#else
|
||||
c.kbd_nav = 0; // T-Deck / V4: keyboard navigation OFF by default (opt-in; persists once toggled on)
|
||||
#endif
|
||||
c.sleep_idle = 0; // default: idle light-sleep OFF
|
||||
{ const char* d = "ertui"; for (int i = 0; i < 5; i++) c.nav_keys[i] = (uint8_t)d[i]; } // default tab hotkeys E/R/T/U/I
|
||||
c.map_zoom_buttons = 0; // default: map zoom = slider
|
||||
#if defined(HAS_TANMATSU)
|
||||
{ const char* d = "wxads"; for (int i = 0; i < 6; i++) c.nav_dir_keys[i] = (uint8_t)d[i]; } // Tanmatsu: W up/X down/A left/D right/S select; no Back letter (Esc/F-key), d[5]='\0'
|
||||
#else
|
||||
{ const char* d = "wzadsq"; for (int i = 0; i < 6; i++) c.nav_dir_keys[i] = (uint8_t)d[i]; } // default W/Z/A/D/S/Q
|
||||
#endif
|
||||
c.home_is_drawer = 0; // default: Home = Commander screen
|
||||
#if defined(HAS_TANMATSU)
|
||||
c.nav_scroll_keys[0] = 'f'; c.nav_scroll_keys[1] = 'v'; // Tanmatsu scroll-up F / scroll-down V
|
||||
#else
|
||||
c.nav_scroll_keys[0] = 'f'; c.nav_scroll_keys[1] = 'c'; // default scroll-up F / scroll-down C
|
||||
#endif
|
||||
c.notify_new_contact = 1; // default: show the new-contact toast (preserve prior behaviour)
|
||||
}
|
||||
|
||||
// Persist the whole blob using the same end()/begin(RW)/put/end()/begin(RO)
|
||||
@@ -192,10 +206,14 @@ static void cfgLoadOrMigrate() {
|
||||
// to the real Custom index on the first touchPrefsGetTimezone() call (the
|
||||
// zone count isn't known here). offset 0 stays zone 0 (Europe) = unchanged.
|
||||
if (s_cfg.ver < 3 && s_cfg.time_offs != 0) s_cfg.tz_zone = 0xFE;
|
||||
// v15: keyboard navigation is now ON by default. Force it on ONCE for existing
|
||||
// users (pre-v15) so they get the feature; their later explicit on/off then
|
||||
// persists (this only fires for ver < 15, never again).
|
||||
if (s_cfg.ver < 15) s_cfg.kbd_nav = 1;
|
||||
// v18: keyboard navigation is now OFF by default (it was force-enabled at v15, but it
|
||||
// complicated the touch UX more than it helped). Reset existing installs to off ONCE so
|
||||
// they match the new default; the user's later explicit on/off then persists (fires only
|
||||
// for ver < 18, never again). The Tanmatsu is exempt — it has no touchscreen, so keyboard
|
||||
// nav is its only input and must stay on.
|
||||
#if !defined(HAS_TANMATSU)
|
||||
if (s_cfg.ver < 18) s_cfg.kbd_nav = 0;
|
||||
#endif
|
||||
s_cfg.ver = TOUCH_CFG_VER;
|
||||
s_cfg.magic = TOUCH_CFG_MAGIC;
|
||||
cfgFlush(); // rewrite with new fields defaulted-in
|
||||
@@ -727,6 +745,17 @@ bool touchPrefsSetHideNodeName(bool hide) {
|
||||
return cfgFlush();
|
||||
}
|
||||
|
||||
bool touchPrefsGetNewContactToast() {
|
||||
if (!s_begun) touchPrefsBegin();
|
||||
return s_cfg.notify_new_contact != 0; // default = show the toast
|
||||
}
|
||||
|
||||
bool touchPrefsSetNewContactToast(bool on) {
|
||||
if (!s_begun) touchPrefsBegin();
|
||||
s_cfg.notify_new_contact = on ? 1 : 0;
|
||||
return cfgFlush();
|
||||
}
|
||||
|
||||
bool touchPrefsGetMapNight() {
|
||||
if (!s_begun) touchPrefsBegin();
|
||||
return s_cfg.map_night != 0;
|
||||
@@ -1217,8 +1246,12 @@ bool touchPrefsGetEnterSends() { if (!s_begun) touchPrefsBegin(); return s_
|
||||
void touchPrefsSetEnterSends(bool on) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("ent_send", on ? 1 : 0); }
|
||||
bool touchPrefsGetClock12h() { if (!s_begun) touchPrefsBegin(); return s_prefs.getUChar("clk_12h", 0) != 0; }
|
||||
void touchPrefsSetClock12h(bool on) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("clk_12h", on ? 1 : 0); }
|
||||
bool touchPrefsGetNavMenubarKeys() { if (!s_begun) touchPrefsBegin(); return s_prefs.getUChar("nav_mbk", 0) != 0; }
|
||||
void touchPrefsSetNavMenubarKeys(bool on) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("nav_mbk", on ? 1 : 0); }
|
||||
bool touchPrefsGetScrollReverse() { if (!s_begun) touchPrefsBegin(); return s_prefs.getUChar("tb_rev", 0) != 0; }
|
||||
void touchPrefsSetScrollReverse(bool on) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("tb_rev", on ? 1 : 0); }
|
||||
bool touchPrefsGetEdgeScroll() { if (!s_begun) touchPrefsBegin(); return s_prefs.getUChar("tb_edgesc", 0) != 0; }
|
||||
void touchPrefsSetEdgeScroll(bool on) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("tb_edgesc", on ? 1 : 0); }
|
||||
bool touchPrefsGetLockOnScreenOff() { if (!s_begun) touchPrefsBegin(); return s_prefs.getUChar("lock_off", 0) != 0; }
|
||||
void touchPrefsSetLockOnScreenOff(bool on) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("lock_off", on ? 1 : 0); }
|
||||
|
||||
|
||||
@@ -148,6 +148,11 @@ bool touchPrefsSetHomeIsDrawer(bool on);
|
||||
bool touchPrefsGetHideNodeName();
|
||||
bool touchPrefsSetHideNodeName(bool hide);
|
||||
|
||||
/** Show the low-key toast/chip when a contact is auto-discovered/added.
|
||||
* Default true (toast shown). Off keeps the diag-log line + tab badge. */
|
||||
bool touchPrefsGetNewContactToast();
|
||||
bool touchPrefsSetNewContactToast(bool on);
|
||||
|
||||
/** Store all device data (identity/prefs/contacts/channels) on the SD card under
|
||||
* /meshcomod instead of internal SPIFFS. T-Deck only; read at boot before data
|
||||
* loads, so changing it requires a reboot. Default false (SPIFFS). */
|
||||
@@ -256,8 +261,12 @@ bool touchPrefsGetEnterSends(); // Enter key sends a chat message
|
||||
void touchPrefsSetEnterSends(bool on);
|
||||
bool touchPrefsGetClock12h(); // 12-hour clock (default false = 24h)
|
||||
void touchPrefsSetClock12h(bool on);
|
||||
bool touchPrefsGetNavMenubarKeys(); // show per-tab nav hotkey letters over the menubar (default false = hidden)
|
||||
void touchPrefsSetNavMenubarKeys(bool on);
|
||||
bool touchPrefsGetScrollReverse(); // invert trackball/scrollball direction (default false)
|
||||
void touchPrefsSetScrollReverse(bool on);
|
||||
bool touchPrefsGetEdgeScroll(); // push cursor past edge to scroll content (default false)
|
||||
void touchPrefsSetEdgeScroll(bool on);
|
||||
bool touchPrefsGetLockOnScreenOff(); // idle screen-off auto-locks; only a deliberate hold wakes (default false)
|
||||
void touchPrefsSetLockOnScreenOff(bool on);
|
||||
|
||||
|
||||
@@ -473,6 +473,27 @@ void setup() {
|
||||
#error "need to define filesystem"
|
||||
#endif
|
||||
|
||||
#if defined(ENV_INCLUDE_GPS) && (ENV_INCLUDE_GPS == 1)
|
||||
// GPS UART resilience (slow / never-acquires TTFF fix): the core opens Serial1 for the
|
||||
// NMEA GPS with Arduino's default 256-byte RX ring — only ~66 ms of slack at 38400 baud
|
||||
// (~270 ms at 9600). A single long LVGL/map frame stalls this loop past that, dropping
|
||||
// UART bytes and corrupting NMEA ephemeris subframes. Each corrupted subframe costs the
|
||||
// receiver ~30 s of re-acquisition, so a busy UI turns a ~1-minute fix into several
|
||||
// minutes — or, with frequent stalls, never. A larger ring absorbs the stalls. MUST
|
||||
// precede the core's Serial1.begin() inside sensors.begin(); setRxBufferSize is a no-op
|
||||
// once the UART is already running.
|
||||
//
|
||||
// Gate on gps_enabled: sensors.begin()'s GPS-detect opens Serial1 on EVERY boot —
|
||||
// including the many V4s with no GPS module — and never calls Serial1.end(), so an
|
||||
// unconditional 4 KB ring permanently costs ~3.8 KB of scarce internal DRAM for nothing
|
||||
// on the GPS-off majority (the "RAM is higher now" reports). GPS-on users (who actually
|
||||
// hit the overflow) still get the big ring; default GPS-off keeps the stock 256 B. A user
|
||||
// who enables GPS mid-session picks it up on the next reboot (gps_enabled is persisted).
|
||||
{
|
||||
auto* np = the_mesh.getNodePrefs();
|
||||
if (np && np->gps_enabled) Serial1.setRxBufferSize(4096);
|
||||
}
|
||||
#endif
|
||||
sensors.begin();
|
||||
|
||||
#ifdef DISPLAY_CLASS
|
||||
@@ -621,6 +642,22 @@ void loop() {
|
||||
}
|
||||
#endif
|
||||
the_mesh.loop();
|
||||
#if defined(GPS_BUF_DEBUG)
|
||||
// Bench diagnostic (build with -DGPS_BUF_DEBUG only; absent in releases): peak GPS UART
|
||||
// backlog accumulated between sensors.loop() drains. A peak above the old 256-byte default
|
||||
// proves loop stalls were overflowing the default ring — i.e. NMEA was being lost, which
|
||||
// is the slow/never-acquires TTFF mechanism. With the 4096 ring above it can climb past
|
||||
// 256 without loss, so a >256 reading is direct proof the fix matters on this unit.
|
||||
{ static size_t s_gps_peak = 0; static uint32_t s_gps_log = 0;
|
||||
size_t bl = Serial1.available();
|
||||
if (bl > s_gps_peak) s_gps_peak = bl;
|
||||
if (millis() - s_gps_log > 5000) {
|
||||
s_gps_log = millis();
|
||||
Serial.printf("[GPSBUF] peak=%u B / 5s (old cap 256, now 4096)\n", (unsigned)s_gps_peak);
|
||||
s_gps_peak = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
sensors.loop();
|
||||
#if defined(ESP32)
|
||||
// GPS time guard (Ricky Leong's "stuck at 1902"): MicroNMEALocationProvider
|
||||
|
||||
+961
-174
File diff suppressed because it is too large.
Load diff
+12
-3
@@ -196,7 +196,8 @@ private:
|
||||
uint16_t in_scope = 0);
|
||||
// Shared core for newMsg / newMsgFromPubWithMeta — see UITask.cpp.
|
||||
void newMsgImpl(uint8_t path_len, const char* from_name, const char* text, int msgcount,
|
||||
uint8_t meta_flags, int8_t snr_q4, int8_t rssi);
|
||||
uint8_t meta_flags, int8_t snr_q4, int8_t rssi,
|
||||
const char* sender_override = nullptr);
|
||||
public:
|
||||
/** Match an arriving ACK (4-byte hash) against the last few outgoing DMs
|
||||
* and flip their delivery state to DELIV_DELIVERED so the chat detail
|
||||
@@ -214,7 +215,7 @@ private:
|
||||
void appendComposerChar(char c);
|
||||
void appendComposerText(const char* text);
|
||||
void backspaceComposerChar();
|
||||
bool sendComposerToActiveThread();
|
||||
bool sendComposerToActiveThread(const char* override_text);
|
||||
void markThreadsDirty(unsigned long delay_ms = 200);
|
||||
void markMsgsDirty(unsigned long delay_ms = 2000);
|
||||
void flushHistoryIfDue(unsigned long now);
|
||||
@@ -285,7 +286,8 @@ public:
|
||||
void composerAppendChar(char c) { appendComposerChar(c); }
|
||||
void composerAppendText(const char* text) { appendComposerText(text); }
|
||||
void composerBackspace() { backspaceComposerChar(); }
|
||||
bool composerSend() { return sendComposerToActiveThread(); }
|
||||
// override_text != nullptr resends that exact text (msg action menu "Resend"); nullptr = composer draft.
|
||||
bool composerSend(const char* override_text = nullptr) { return sendComposerToActiveThread(override_text); }
|
||||
void composerTypingMode() { _composer_action_idx = -1; }
|
||||
/** Open DM thread for mesh contact index (e.g. Chats thread list / external hooks). */
|
||||
void openMeshContactDm(uint32_t mesh_contact_index);
|
||||
@@ -402,6 +404,13 @@ public:
|
||||
const uint8_t* from_pub, const char* from_name,
|
||||
const char* text, int msgcount,
|
||||
int8_t snr_q4, int8_t rssi) override;
|
||||
void newRoomMsgFromPubWithMeta(uint8_t path_len, bool is_flood,
|
||||
const uint8_t* from_pub, const char* from_name,
|
||||
const char* author_name,
|
||||
const char* text, int msgcount,
|
||||
int8_t snr_q4, int8_t rssi) override;
|
||||
void appSentMsgToContact(const uint8_t* to_pub, const char* to_name, const char* text,
|
||||
uint32_t ack_hash) override;
|
||||
void notify(UIEventType t = UIEventType::none) override;
|
||||
void logRxFrame(float snr, float rssi, const uint8_t* raw, int len) override;
|
||||
void discoveredContact(const ContactInfo& contact, bool is_new, uint8_t path_len) override;
|
||||
|
||||
@@ -505,6 +505,10 @@ static const lv_img_dsc_t d_1f40c = { { LV_IMG_CF_TRUE_COLOR_ALPHA, 0, 0, 16, 16
|
||||
static const uint8_t e_1f41e[] = {0,0,1,0,0,0,31,0,1,0,0,1,0,0,1,44,99,5,0,0,0,110,107,102,110,107,74,110,99,133,0,0,0,170,82,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,78,107,22,0,0,0,0,0,0,0,0,0,0,0,0,239,115,39,110,107,115,142,107,34,110,107,193,77,107,60,0,0,1,239,123,2,0,0,0,0,0,0,0,0,0,77,107,17,142,107,91,110,107,111,115,140,20,146,124,14,239,115,86,203,82,162,45,99,81,0,0,0,175,115,150,110,107,106,170,170,3,239,123,4,0,0,0,0,0,0,0,0,0,110,107,103,110,99,113,110,107,58,106,74,134,203,82,209,110,107,255,167,49,255,17,173,246,146,189,159,73,66,207,255,47,6,0,0,0,0,248,2,0,0,0,0,0,0,0,0,0,78,107,43,110,99,160,0,0,0,80,148,150,138,82,255,69,41,248,69,41,253,7,66,255,40,74,255,227,184,253,195,248,176,138,186,63,0,0,0,231,121,4,239,123,4,0,0,0,109,107,41,110,107,192,174,83,70,20,190,230,7,50,253,69,25,254,134,33,255,69,33,254,227,192,251,227,248,255,4,177,255,199,169,255,77,123,112,0,0,0,0,0,0,0,0,0,0,0,0,207,115,66,105,122,224,36,137,255,37,129,253,69,121,255,101,73,255,69,161,255,227,248,255,227,248,255,101,57,251,36,81,254,8,194,244,207,91,107,110,107,113,0,0,0,0,248,1,175,91,88,199,209,227,195,208,252,227,248,254,195,248,255,228,248,255,37,241,255,4,193,255,101,73,255,69,129,254,227,216,254,37,233,241,142,99,130,110,107,87,0,0,0,0,0,0,142,115,149,8,122,255,101,9,252,36,185,255,69,105,255,69,89,255,69,225,255,69,129,255,134,1,255,69,105,255,195,248,254,4,241,248,44,123,145,0,0,0,0,0,0,0,0,0,175,99,125,199,161,248,4,145,255,36,177,254,134,17,255,134,17,255,69,177,255,37,225,255,36,153,255,36,177,255,195,240,252,37,249,255,45,115,213,239,83,6,0,0,0,44,99,10,175,91,168,166,209,178,163,248,255,227,248,254,36,153,255,36,145,255,227,248,255,37,249,255,4,209,255,134,17,255,36,129,251,101,249,255,77,107,170,0,0,0,0,0,0,110,107,155,142,91,126,171,146,138,4,249,255,227,248,252,228,224,255,101,81,255,4,169,255,36,249,255,36,193,255,101,49,254,4,161,252,102,233,234,143,91,206,77,107,81,0,0,0,77,107,12,0,0,0,174,91,211,167,201,213,162,248,255,36,169,252,134,1,252,69,105,254,4,249,254,36,241,253,195,248,249,4,249,255,195,248,88,207,83,46,77,107,115,0,0,0,0,0,0,170,82,3,110,99,58,110,99,202,4,241,177,228,248,255,36,193,255,4,233,255,227,248,255,37,241,255,4,249,255,227,248,177,10,248,3,0,0,0,0,0,0,0,0,0,239,123,2,0,0,0,110,107,51,110,99,197,243,44,16,163,248,89,227,248,192,227,248,231,227,248,235,36,241,209,4,241,127,69,249,6,0,248,1,170,170,3,181,82,3,0,0,0,239,123,2,170,82,3,110,107,161,78,107,50,0,0,0,0,0,0,0,0,0,195,224,19,4,249,23,0,248,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0};
|
||||
static const lv_img_dsc_t d_1f41e = { { LV_IMG_CF_TRUE_COLOR_ALPHA, 0, 0, 16, 16 }, sizeof(e_1f41e), e_1f41e };
|
||||
|
||||
static const uint8_t e_1f37a[] = {0,0,0,0,0,0,255,255,2,0,0,0,46,254,22,55,255,178,186,255,232,121,255,208,212,254,81,0,0,0,255,255,2,0,248,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,255,147,121,255,223,255,255,255,255,255,255,255,255,255,154,255,251,178,254,69,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,212,254,98,253,255,255,255,255,255,255,255,253,255,255,255,255,255,253,255,255,255,187,255,255,56,255,199,80,246,19,255,255,1,255,255,1,0,0,0,0,0,0,0,0,0,0,0,0,56,255,241,255,255,252,255,255,252,255,255,255,255,255,255,255,255,255,255,255,253,255,255,255,122,255,255,146,246,34,255,255,3,255,255,3,0,0,0,0,0,0,0,0,0,0,0,0,25,239,167,55,255,255,189,255,253,222,255,255,89,255,255,89,255,255,56,255,253,55,255,254,247,238,216,255,157,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,230,125,89,255,255,56,255,252,23,255,255,81,238,255,82,238,255,49,238,254,172,237,255,84,238,226,255,255,193,223,255,156,28,231,27,0,0,0,0,0,0,0,0,0,0,0,0,176,229,131,89,255,255,43,229,252,166,228,255,134,220,255,69,220,255,68,220,255,192,219,255,111,213,240,25,190,237,218,222,255,93,239,160,0,0,0,0,0,0,0,0,0,0,0,0,241,229,137,122,255,255,101,220,252,160,219,255,2,220,255,164,228,255,226,219,253,34,228,255,110,221,206,206,74,20,23,198,136,60,231,223,0,0,0,0,0,0,0,0,0,0,0,0,17,230,138,154,255,255,134,228,252,2,220,255,100,220,255,100,228,255,2,220,253,97,236,255,206,229,202,0,0,0,92,239,82,60,231,228,0,0,0,0,0,0,0,0,0,0,0,0,49,238,142,153,255,255,197,228,253,98,228,255,196,228,255,33,220,255,98,228,253,193,244,255,238,237,200,0,0,0,92,239,136,219,222,223,0,0,0,0,0,0,0,0,0,0,0,0,146,238,145,219,255,255,36,237,253,35,237,255,69,237,255,161,236,255,194,236,253,33,253,255,13,238,202,255,255,93,125,239,245,150,181,167,0,0,0,0,0,0,0,0,0,0,0,0,113,238,149,50,255,255,131,245,252,33,245,255,99,245,255,132,245,255,33,245,254,96,253,255,79,238,242,94,231,255,150,181,217,239,123,38,0,0,0,0,0,0,0,0,0,0,0,0,218,222,158,197,245,255,128,253,254,161,253,255,228,253,255,161,253,255,128,253,255,162,253,255,86,214,212,212,148,99,140,115,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,94,239,219,26,231,254,136,229,245,130,245,252,129,245,253,98,245,253,134,229,246,85,214,237,61,231,208,0,0,0,0,0,0,85,173,3,0,0,0,0,0,0,0,0,0,0,0,0,251,222,163,28,223,252,252,222,255,121,214,247,87,214,249,120,214,243,187,214,252,28,223,255,251,222,195,251,222,15,239,255,2,255,255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,231,71,251,230,162,252,222,207,252,222,226,252,222,221,251,222,194,27,231,118,125,239,16,0,0,0,255,255,1,0,0,0,0,0,0,0,0,0};
|
||||
static const lv_img_dsc_t d_1f37a = { { LV_IMG_CF_TRUE_COLOR_ALPHA, 0, 0, 16, 16 }, sizeof(e_1f37a), e_1f37a };
|
||||
static const uint8_t e_1f37b[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,2,0,0,0,68,249,65,100,249,52,101,249,94,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,239,251,2,115,254,5,197,249,48,100,249,86,36,249,142,64,248,61,114,255,14,89,255,119,154,255,137,186,255,126,56,255,66,0,0,0,85,253,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,32,2,249,106,102,250,63,220,255,165,221,255,233,255,255,255,255,255,255,255,255,255,155,255,237,192,252,5,0,0,0,255,255,2,255,255,2,255,255,2,89,255,39,187,255,166,252,255,158,253,255,193,187,255,219,254,255,255,255,255,255,255,255,252,255,255,249,221,255,251,123,255,215,254,255,24,255,255,19,0,0,0,255,255,5,0,0,0,88,255,115,255,255,255,255,255,255,255,255,255,255,255,255,189,255,252,156,255,254,189,255,255,121,255,254,212,254,255,216,238,224,187,214,200,186,214,215,125,239,50,0,0,0,255,255,42,91,247,117,188,255,250,154,255,249,122,255,253,255,255,253,255,255,255,255,255,255,121,255,255,49,246,254,232,228,254,76,229,255,30,215,94,121,214,105,60,231,158,158,247,100,251,222,234,252,222,221,240,237,252,101,228,255,199,228,255,122,255,255,23,255,255,122,255,253,174,237,254,100,220,255,2,220,253,165,236,255,26,239,140,255,255,10,60,231,189,60,231,185,85,181,43,191,247,165,106,245,255,192,219,252,7,229,255,214,246,255,2,220,255,142,229,239,35,220,253,67,220,255,131,228,254,224,244,255,113,238,212,127,231,143,121,214,209,93,239,174,0,0,0,156,247,209,70,245,255,2,237,253,112,246,255,48,246,255,224,219,252,115,238,237,80,246,255,225,236,255,165,245,254,96,253,254,73,254,245,187,214,211,51,165,67,218,222,202,61,223,142,90,247,238,98,253,255,131,245,254,199,245,255,68,245,255,6,237,247,213,238,228,51,255,255,195,253,255,226,253,255,160,253,255,199,237,251,220,214,183,255,255,5,84,173,70,123,206,189,142,254,250,96,253,255,193,253,255,96,253,255,64,253,255,201,237,231,95,231,136,176,246,246,229,253,251,130,245,249,138,221,246,121,214,255,28,223,203,90,239,13,0,0,0,158,247,142,181,238,250,112,238,241,78,238,245,9,246,246,226,253,254,78,246,189,255,231,54,184,222,255,53,214,255,120,214,255,188,214,251,61,231,186,93,247,36,0,0,0,121,206,5,154,214,157,57,198,240,58,198,251,90,198,251,123,206,247,185,222,255,251,230,188,255,231,10,61,231,119,62,231,158,61,231,127,124,247,62,0,0,0,0,0,0,255,255,1,0,0,0,170,82,6,181,181,79,182,189,140,214,189,176,214,189,195,248,189,197,154,214,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,2,0,0,0};
|
||||
static const lv_img_dsc_t d_1f37b = { { LV_IMG_CF_TRUE_COLOR_ALPHA, 0, 0, 16, 16 }, sizeof(e_1f37b), e_1f37b };
|
||||
typedef struct { uint32_t cp; const lv_img_dsc_t* dsc; } EmojiGlyph;
|
||||
static const EmojiGlyph kGlyphs[] = {
|
||||
{ 0x200Du, &d_zero },
|
||||
@@ -554,6 +558,8 @@ static const EmojiGlyph kGlyphs[] = {
|
||||
{ 0x1F340u, &d_1f340 },
|
||||
{ 0x1F341u, &d_1f341 },
|
||||
{ 0x1F342u, &d_1f342 },
|
||||
{ 0x1F37Au, &d_1f37a },
|
||||
{ 0x1F37Bu, &d_1f37b },
|
||||
{ 0x1F381u, &d_1f381 },
|
||||
{ 0x1F388u, &d_1f388 },
|
||||
{ 0x1F389u, &d_1f389 },
|
||||
@@ -761,7 +767,7 @@ static const EmojiGlyph kGlyphs[] = {
|
||||
{ 0x1F9D0u, &d_1f9d0 },
|
||||
{ 0x1F9E1u, &d_1f9e1 },
|
||||
};
|
||||
const uint16_t kEmojiGlyphCount = 250;
|
||||
const uint16_t kEmojiGlyphCount = 252;
|
||||
|
||||
const lv_img_dsc_t* emojiGlyphLookup(uint32_t cp) {
|
||||
int lo = 0, hi = (int)(sizeof(kGlyphs)/sizeof(kGlyphs[0])) - 1;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// AUTO-GENERATED by scripts/build/gen-emoji-font.py — do not edit.
|
||||
// 250 Noto colour emoji baked to RGB565+alpha at 16px.
|
||||
// 252 Noto colour emoji baked to RGB565+alpha at 16px.
|
||||
#pragma once
|
||||
#include "lvgl.h"
|
||||
#ifdef __cplusplus
|
||||
|
||||
Reference in new issue
Block a user