{
  "id": "meck-p4",
  "name": "Meck-P4",
  "type": "fork",
  "maintainer": "pelgraine",
  "description": "A port of Meck (a MeshCore fork) to the LilyGo T-Display P4, targeting the ESP32-P4 main MCU. The onboard ESP32-C6 provides WiFi companion connectivity to the MeshCore app; BLE is not yet enabled. Features a full touch UI with virtual keyboard, channel messaging, repeater admin, trace route, position adverts, and private channels.\n",
  "repository": "https://github.com/pelgraine/Meck-P4",
  "license": "GPL-3.0",
  "status": "active",
  "lifecycle": "active",
  "maturity": "alpha",
  "distribution": "community",
  "lineage": {
    "kind": "fork",
    "upstreamFirmwareId": "meshcore-official",
    "upstreamRepository": "https://github.com/meshcore-dev/MeshCore"
  },
  "runtime": {
    "framework": "arduino",
    "language": "cpp"
  },
  "roles": [
    "companion",
    "repeater",
    "room-server"
  ],
  "features": [
    "ESP32-P4 main MCU with ESP32-C6 WiFi companion",
    "Touch UI with virtual keyboard",
    "Screen-off power saving",
    "Channel messaging and direct messages",
    "Repeater admin and room server",
    "Trace route and path view",
    "Position adverts and share position",
    "Private channels",
    "Per-contact path editor",
    "Region scope"
  ],
  "capabilities": {
    "protocol": {
      "meshcoreCompatible": true
    },
    "transports": {
      "ble": false,
      "usbSerial": true,
      "nativeTcp": true,
      "wifiAp": true
    },
    "operations": {
      "ota": false,
      "webFlasher": false
    },
    "networking": {
      "repeater": true,
      "roomServer": true,
      "observer": false,
      "kissModem": false
    },
    "hardware": {
      "gps": true,
      "display": true,
      "sensors": false,
      "lowPowerRx": false
    }
  },
  "devices": [
    {
      "id": "lilygo-tdisplay-p4",
      "status": "supported"
    }
  ],
  "popularity": {
    "githubStars": 9,
    "githubForks": 1,
    "githubWatchers": 1,
    "githubOpenIssues": 0,
    "githubContributors": 2,
    "releaseDownloads": 111,
    "latestReleaseDownloads": 17,
    "lastChecked": "2026-06-21"
  },
  "verification": {
    "sourceAvailable": true,
    "releasesAvailable": true,
    "ciBuilds": false,
    "lastChecked": "2026-06-21"
  },
  "source": {
    "path": "data/firmwares/meck-p4/firmware.yaml",
    "updatedAt": "2026-06-22T21:31:07+02:00"
  },
  "latest_version": "0.6.2-patch",
  "released": "2026-06-14",
  "releases": [
    {
      "version": "v0.6.2-patch",
      "name": "Meck P4 v0.6.2 Pre-release (Patch): Trace and notification fixes",
      "datetime": "2026-06-14T19:48:10Z",
      "url": "https://github.com/pelgraine/Meck-P4/releases/tag/v0.6.2-patch",
      "prerelease": true,
      "notes": "# Meck P4 v0.6.2 Pre-release (Patch): Trace and notification fixes\r\n\r\nPoint release on top of [[v0.6.1](https://github.com/pelgraine/Meck-P4/releases/tag/v0.6.1-patch)](https://github.com/pelgraine/Meck-P4/releases/tag/v0.6.1-patch). Two bug fixes, no new features. Firmware identity is now 0.6.2.\r\n\r\n## Bug Fixes\r\n\r\n### Multi-byte trace path parsed as single-byte\r\n\r\nRunning a path trace in **2-byte mode** returned a wrong result. The response was parsed as if it were 1-byte, so it reported twice as many hops as actually existed, each one resolved to the wrong repeater. As an example, a two-hop 2-byte trace of `3601,2198` came back as four hops (`36`, `01`, `21`, `98`) matched against unrelated repeaters.\r\n\r\nThe cause was in the trace-response handler: the hop count and hash size were taken from the wrong source. The byte length handed up from the base layer was being read with the packet-header path encoding, which collapses to 1-byte mode for any short path. The real hash size is carried separately in the trace flags.\r\n\r\nThe handler now reads the hash size from the trace flags and computes the hop count as the path byte length divided by bytes-per-hop. 1-byte traces were already correct and are unchanged. 2-byte traces now report the right hop count and resolve to the correct repeaters.\r\n\r\n### Custom notification sounds not playing\r\n\r\nCustom per-channel notification tones stopped playing as of v0.6 (they worked in v0.5). A tone could be selected and would show as set, but nothing played when a message arrived on that channel.\r\n\r\nThe cause was the v0.6 audio rework, which split file preparation from playback start. The notification trigger prepared the tone file but never issued the start, so it sat ready and silent.\r\n\r\nThe notification path now starts playback after preparing. The same missing start also affected the tone-picker preview and voice-message playback, both fixed by the same change.\r\n\r\n## Files Changed\r\n\r\n3 files changed (`MeckMesh.h`, `MeckUI.cpp`, `meck.h`).\r\n\r\n## First-Time Flashing: Read This First\r\n\r\nMeck-P4 ships as a single **merged binary** (bootloader + partition table + application combined). One file, flash at offset `0x0`.\r\n> **An SD card is recommended.** With a FAT32-formatted card inserted, every saved setting, channel message, DM, room post, and note is mirrored or stored on the card, notification tones have somewhere to live, and the device recovers gracefully from a wiped NVS. Without an SD card the device still works but loses message history on reboot, and the Reader, Notes, and notification sounds have no storage.\r\n\r\nEnsure you use the **right-side USB-C port** (the data port), not the high-speed charger port, to flash.\r\n<img width=\"377\" height=\"284\" alt=\"correct port usage for flashing\" src=\"https://github.com/user-attachments/assets/d3db41b5-0a93-47fb-9edc-c3271d7ac6af\" />\r\n\r\n### Flashing with the MeshCore Web Flasher (recommended)\r\n\r\n1. Go to <https://flasher.meshcore.io/>\r\n2. Scroll to the bottom and select **Custom Firmware**\r\n3. Select the `meck-p4-0.6.2.bin` file you downloaded\r\n4. Click OK on the merged-binary warning\r\n5. Click **Flash**, pick your device in the popup, and click **Connect**\r\n\r\n### Flashing with esptool.py\r\n\r\n```\r\npip install esptool\r\nesptool.py --chip esp32p4 -p PORT write_flash 0x0 meck-p4-0.6.2.bin\r\n```\r\n\r\n(Replace `PORT` with `/dev/cu.usbmodemXXXX` on macOS, `/dev/ttyACM0` on Linux, or `COM3` on Windows.)\r\n\r\nUpgrading from v0.6.1 or v0.6 does not require `erase_flash`. This patch changes program logic only and adds no new settings, so existing prefs carry over unchanged.\r\n\r\n---\r\n\r\nFor the full feature list see the [[README](https://github.com/pelgraine/Meck-P4/blob/main/README.md)](https://github.com/pelgraine/Meck-P4/blob/main/README.md).\r\n\r\n## Reporting Issues\r\n\r\nThe Meck-P4 channel on the MeshCore Discord is the fastest path. GitHub Issues on the Meck-P4 repo also work for anything reproducible. Include the serial log if you can: Settings > Debug Logs > Start capt\n…",
      "notesHtml": "<h1>Meck P4 v0.6.2 Pre-release (Patch): Trace and notification fixes</h1>\n<p>Point release on top of <a href=\"https://github.com/pelgraine/Meck-P4/releases/tag/v0.6.1-patch\" target=\"_blank\" rel=\"noopener noreferrer\"><a href=\"https://github.com/pelgraine/Meck-P4/releases/tag/v0.6.1-patch\" target=\"_blank\" rel=\"noopener noreferrer\">v0.6.1</a></a>. Two bug fixes, no new features. Firmware identity is now 0.6.2.</p>\n<h2>Bug Fixes</h2>\n<h3>Multi-byte trace path parsed as single-byte</h3>\n<p>Running a path trace in <strong>2-byte mode</strong> returned a wrong result. The response was parsed as if it were 1-byte, so it reported twice as many hops as actually existed, each one resolved to the wrong repeater. As an example, a two-hop 2-byte trace of <code>3601,2198</code> came back as four hops (<code>36</code>, <code>01</code>, <code>21</code>, <code>98</code>) matched against unrelated repeaters.</p>\n<p>The cause was in the trace-response handler: the hop count and hash size were taken from the wrong source. The byte length handed up from the base layer was being read with the packet-header path encoding, which collapses to 1-byte mode for any short path. The real hash size is carried separately in the trace flags.</p>\n<p>The handler now reads the hash size from the trace flags and computes the hop count as the path byte length divided by bytes-per-hop. 1-byte traces were already correct and are unchanged. 2-byte traces now report the right hop count and resolve to the correct repeaters.</p>\n<h3>Custom notification sounds not playing</h3>\n<p>Custom per-channel notification tones stopped playing as of v0.6 (they worked in v0.5). A tone could be selected and would show as set, but nothing played when a message arrived on that channel.</p>\n<p>The cause was the v0.6 audio rework, which split file preparation from playback start. The notification trigger prepared the tone file but never issued the start, so it sat ready and silent.</p>\n<p>The notification path now starts playback after preparing. The same missing start also affected the tone-picker preview and voice-message playback, both fixed by the same change.</p>\n<h2>Files Changed</h2>\n<p>3 files changed (<code>MeckMesh.h</code>, <code>MeckUI.cpp</code>, <code>meck.h</code>).</p>\n<h2>First-Time Flashing: Read This First</h2>\n<p>Meck-P4 ships as a single <strong>merged binary</strong> (bootloader + partition table + application combined). One file, flash at offset <code>0x0</code>.</p>\n<blockquote>\n<p><strong>An SD card is recommended.</strong> With a FAT32-formatted card inserted, every saved setting, channel message, DM, room post, and note is mirrored or stored on the card, notification tones have somewhere to live, and the device recovers gracefully from a wiped NVS. Without an SD card the device still works but loses message history on reboot, and the Reader, Notes, and notification sounds have no storage.</p>\n</blockquote>\n<p>Ensure you use the <strong>right-side USB-C port</strong> (the data port), not the high-speed charger port, to flash.\n<img alt=\"correct port usage for flashing\" src=\"https://github.com/user-attachments/assets/d3db41b5-0a93-47fb-9edc-c3271d7ac6af\" /></p>\n<h3>Flashing with the MeshCore Web Flasher (recommended)</h3>\n<ol>\n<li>Go to <a href=\"https://flasher.meshcore.io/\" target=\"_blank\" rel=\"noopener noreferrer\">https://flasher.meshcore.io/</a></li>\n<li>Scroll to the bottom and select <strong>Custom Firmware</strong></li>\n<li>Select the <code>meck-p4-0.6.2.bin</code> file you downloaded</li>\n<li>Click OK on the merged-binary warning</li>\n<li>Click <strong>Flash</strong>, pick your device in the popup, and click <strong>Connect</strong></li>\n</ol>\n<h3>Flashing with esptool.py</h3>\n<pre><code>pip install esptool\nesptool.py --chip esp32p4 -p PORT write_flash 0x0 meck-p4-0.6.2.bin\n</code></pre>\n<p>(Replace <code>PORT</code> with <code>/dev/cu.usbmodemXXXX</code> on macOS, <code>/dev/ttyACM0</code> on Linux, or <code>COM3</code> on Windows.)</p>\n<p>Upgrading from v0.6.1 or v0.6 does not require <code>erase_flash</code>. This patch changes program logic only and adds no new settings, so existing prefs carry over unchanged.</p>\n<hr />\n<p>For the full feature list see the <a href=\"https://github.com/pelgraine/Meck-P4/blob/main/README.md\" target=\"_blank\" rel=\"noopener noreferrer\"><a href=\"https://github.com/pelgraine/Meck-P4/blob/main/README.md\" target=\"_blank\" rel=\"noopener noreferrer\">README</a></a>.</p>\n<h2>Reporting Issues</h2>\n<p>The Meck-P4 channel on the MeshCore Discord is the fastest path. GitHub Issues on the Meck-P4 repo also work for anything reproducible. Include the serial log if you can: Settings &gt; Debug Logs &gt; Start capt\n…</p>\n"
    },
    {
      "version": "v0.6.1-patch",
      "name": "Meck P4 v0.6.1 Pre-release (Patch): Antenna switch",
      "datetime": "2026-06-10T03:42:22Z",
      "url": "https://github.com/pelgraine/Meck-P4/releases/tag/v0.6.1-patch",
      "prerelease": true,
      "notes": "Point release on top of [v0.6](https://github.com/pelgraine/Meck-P4/releases/tag/v0.6), adding a software LoRa antenna selector in Settings. No other functional changes. Firmware identity is now 0.6.1.\r\n\r\n## Antenna Switch\r\n\r\nA new **Antenna (tap to toggle)** row at the top of **Settings**, switching the SX1262 between the two ports on the SKY13453 RF switch (XL9535 IO1 / VCTL).\r\n\r\n- **Internal (RF1)** is the default and corresponds to VCTL HIGH; **External (RF2)** is VCTL LOW. The internal antenna is the one in use when no external antenna is attached, so the device works out of the box without changing this.\r\n- The choice persists via prefs (NVS, mirrored to SD) and is re-applied at boot, so the selected port survives a reboot.\r\n- Tap applies live: the pin is driven immediately and the row label updates to show the active port.\r\n\r\n## Bug Fixes\r\n\r\nNone.\r\n\r\n## Files Changed\r\n\r\n5 files changed.\r\n\r\n## First-Time Flashing: Read This First\r\n\r\nMeck-P4 ships as a single **merged binary** (bootloader + partition table + application combined). One file, flash at offset `0x0`.\r\n> **An SD card is recommended.** With a FAT32-formatted card inserted, every saved setting, channel message, DM, room post, and note is mirrored or stored on the card, notification tones have somewhere to live, and the device recovers gracefully from a wiped NVS. Without an SD card the device still works but loses message history on reboot, and the Reader, Notes, and notification sounds have no storage.\r\n\r\nEnsure you use the **right-side USB-C port** (the data port), not the high-speed charger port, to flash.\r\n\r\n### Flashing with the MeshCore Web Flasher (recommended)\r\n\r\n1. Go to <https://flasher.meshcore.io/>\r\n2. Scroll to the bottom and select **Custom Firmware**\r\n3. Select the `meck-p4-0.6.1.bin` file you downloaded\r\n4. Click OK on the merged-binary warning\r\n5. Click **Flash**, pick your device in the popup, and click **Connect**\r\n\r\n### Flashing with esptool.py\r\n\r\n```\r\npip install esptool\r\nesptool.py --chip esp32p4 -p PORT write_flash 0x0 meck-p4-0.6.1.bin\r\n```\r\n\r\n(Replace `PORT` with `/dev/cu.usbmodemXXXX` on macOS, `/dev/ttyACM0` on Linux, or `COM3` on Windows.)\r\n\r\nUpgrading from v0.6 does not require `erase_flash`. The prefs loader tolerates the older layout, and the new antenna field comes up at its default (Internal).\r\n\r\n---\r\n\r\nFor the full feature list see the [[README](https://github.com/pelgraine/Meck-P4/blob/main/README.md)](https://github.com/pelgraine/Meck-P4/blob/main/README.md).\r\n\r\n## Reporting Issues\r\n\r\nThe Meck-P4 channel on the MeshCore Discord is the fastest path. GitHub Issues on the Meck-P4 repo also work for anything reproducible. Include the serial log if you can: Settings > Debug Logs > Start captures it to SD if a serial monitor isn't practical.\r\n\r\n## License\r\n\r\nMIT for Meck-specific code. The combined firmware binary links libraries with mixed licensing including GPL-3.0 and LGPL-2.1 (Codec2) and is effectively GPL-3.0 when distributed.",
      "notesHtml": "<p>Point release on top of <a href=\"https://github.com/pelgraine/Meck-P4/releases/tag/v0.6\" target=\"_blank\" rel=\"noopener noreferrer\">v0.6</a>, adding a software LoRa antenna selector in Settings. No other functional changes. Firmware identity is now 0.6.1.</p>\n<h2>Antenna Switch</h2>\n<p>A new <strong>Antenna (tap to toggle)</strong> row at the top of <strong>Settings</strong>, switching the SX1262 between the two ports on the SKY13453 RF switch (XL9535 IO1 / VCTL).</p>\n<ul>\n<li><strong>Internal (RF1)</strong> is the default and corresponds to VCTL HIGH; <strong>External (RF2)</strong> is VCTL LOW. The internal antenna is the one in use when no external antenna is attached, so the device works out of the box without changing this.</li>\n<li>The choice persists via prefs (NVS, mirrored to SD) and is re-applied at boot, so the selected port survives a reboot.</li>\n<li>Tap applies live: the pin is driven immediately and the row label updates to show the active port.</li>\n</ul>\n<h2>Bug Fixes</h2>\n<p>None.</p>\n<h2>Files Changed</h2>\n<p>5 files changed.</p>\n<h2>First-Time Flashing: Read This First</h2>\n<p>Meck-P4 ships as a single <strong>merged binary</strong> (bootloader + partition table + application combined). One file, flash at offset <code>0x0</code>.</p>\n<blockquote>\n<p><strong>An SD card is recommended.</strong> With a FAT32-formatted card inserted, every saved setting, channel message, DM, room post, and note is mirrored or stored on the card, notification tones have somewhere to live, and the device recovers gracefully from a wiped NVS. Without an SD card the device still works but loses message history on reboot, and the Reader, Notes, and notification sounds have no storage.</p>\n</blockquote>\n<p>Ensure you use the <strong>right-side USB-C port</strong> (the data port), not the high-speed charger port, to flash.</p>\n<h3>Flashing with the MeshCore Web Flasher (recommended)</h3>\n<ol>\n<li>Go to <a href=\"https://flasher.meshcore.io/\" target=\"_blank\" rel=\"noopener noreferrer\">https://flasher.meshcore.io/</a></li>\n<li>Scroll to the bottom and select <strong>Custom Firmware</strong></li>\n<li>Select the <code>meck-p4-0.6.1.bin</code> file you downloaded</li>\n<li>Click OK on the merged-binary warning</li>\n<li>Click <strong>Flash</strong>, pick your device in the popup, and click <strong>Connect</strong></li>\n</ol>\n<h3>Flashing with esptool.py</h3>\n<pre><code>pip install esptool\nesptool.py --chip esp32p4 -p PORT write_flash 0x0 meck-p4-0.6.1.bin\n</code></pre>\n<p>(Replace <code>PORT</code> with <code>/dev/cu.usbmodemXXXX</code> on macOS, <code>/dev/ttyACM0</code> on Linux, or <code>COM3</code> on Windows.)</p>\n<p>Upgrading from v0.6 does not require <code>erase_flash</code>. The prefs loader tolerates the older layout, and the new antenna field comes up at its default (Internal).</p>\n<hr />\n<p>For the full feature list see the <a href=\"https://github.com/pelgraine/Meck-P4/blob/main/README.md\" target=\"_blank\" rel=\"noopener noreferrer\"><a href=\"https://github.com/pelgraine/Meck-P4/blob/main/README.md\" target=\"_blank\" rel=\"noopener noreferrer\">README</a></a>.</p>\n<h2>Reporting Issues</h2>\n<p>The Meck-P4 channel on the MeshCore Discord is the fastest path. GitHub Issues on the Meck-P4 repo also work for anything reproducible. Include the serial log if you can: Settings &gt; Debug Logs &gt; Start captures it to SD if a serial monitor isn't practical.</p>\n<h2>License</h2>\n<p>MIT for Meck-specific code. The combined firmware binary links libraries with mixed licensing including GPL-3.0 and LGPL-2.1 (Codec2) and is effectively GPL-3.0 when distributed.</p>\n"
    },
    {
      "version": "v0.6",
      "name": "Meck P4 v0.6 Pre-release: Web reader, Notes, and EPUB books",
      "datetime": "2026-06-09T22:14:40Z",
      "url": "https://github.com/pelgraine/Meck-P4/releases/tag/v0.6",
      "prerelease": true,
      "notes": "Feature release building on v0.5, adding a reader-mode web browser over the ESP32-C6, a Notes app, EPUB book support in the reader, a Cyrillic keyboard layout, optional M5Stack CardKB hardware-keyboard support, and a portrait/landscape orientation toggle, alongside several bug fixes. Firmware identity is now 0.6.\r\n\r\n## Web Reader\r\n\r\nA lightweight reader-mode web browser on the new **Web** home tile, running over the onboard ESP32-C6. It drives the C6 directly over ESP-AT, building its HTTP and TLS requests by hand, so it shares the WiFi companion's C6 link but uses its own request path. The C6 must be connected to WiFi for it to fetch anything.\r\n\r\n- Landing menu with **Enter URL**, a **DuckDuckGo Lite** search, **Bookmarks**, and **History**.\r\n- Reader view: pages are parsed to readable text with a status line. Headings render in dark red; link markers show as a muted grey `[N]` and are followed from the **Links** panel.\r\n- HTTPS support, confirmed against DuckDuckGo Lite.\r\n- GET form fill: an orange **Forms (N)** button opens a picker (duplicate forms collapsed), then a fill modal with a labelled field per text or password input. Submitting builds the `action?name=value&...` query and loads it through the normal page path, so history and status stay correct.\r\n- Bookmarks (added from the reader view with a confirmation toast) and History.\r\n- This is a **work in progress**: an amber notice appears for a few seconds each time the Web tile is opened. See Known Limitations below and the README for the full list.\r\n\r\n## EPUB Reading\r\n\r\nThe reader now opens **EPUB (`.epub`)** files in addition to plain text. The file list shows folders, `.txt`, and `.epub` items.\r\n\r\n- Tapping an EPUB converts it to plain text the first time it is opened. A *Converting ... to txt* screen is shown while the book is decoded.\r\n- The result is cached as a `.txt` in a hidden `.epub_cache` subfolder, so re-opening the same EPUB later loads straight from the cache and the conversion only runs once.\r\n- You only ever see and tap the `.epub` itself; the cached text stays hidden from the browser.\r\n\r\n## Notes\r\n\r\nA Notes app on the **Notes** home tile. Notes are plain UTF-8 `.txt` files stored under `/sdcard/notes`, so they live on the removable card, survive a reflash, and can be read or edited on a computer. The folder is created automatically on first run.\r\n\r\n- Browser modelled on the reader's: folder navigation, an up-one-level button, and a breadcrumb, rooted at `/sdcard/notes`. A green **+ New Note** row sits at the top of every folder.\r\n- Create and edit with the on-screen keyboard (which honours the KB Theme and KB Layout settings). New notes are named by date and time when the clock is synced (`note_YYYYMMDD_HHMM.txt`), or sequentially (`note_NNN.txt`) otherwise.\r\n- Read view reuses the reader's paging: left third of the screen turns back, right two-thirds turns forward, with a progress percentage and a saved-position bookmark.\r\n- Long-press a note for an action menu with **Rename**, **Delete** (with confirmation), and **Cancel**.\r\n\r\n## Cyrillic Keyboard\r\n\r\nA fourth on-screen keyboard layout, **ЙЦУКЕН** (Cyrillic), added to the **Settings > KB Layout** cycle alongside QWERTY, AZERTY, and QWERTZ.\r\n\r\n- Standard Russian arrangement, also commonly used by Bulgarian users. The top three rows carry twelve letters each, with ё on the third row.\r\n- The control keys keep their Latin labels (ABC / abc / 1#) so case and number switching still work.\r\n- Ukrainian and Serbian letter sets are not included. The Settings row shows \"ЙЦУКЕН\" when this layout is active.\r\n\r\n## Physical Keyboard (M5Stack CardKB)\r\n\r\nOptional support for the **M5Stack CardKB**, a small I2C QWERTY keyboard, as an alternative to the on-screen keyboard for typing messages. Gated behind the `MECK_CARDKB` build flag (a build-from-source option).\r\n\r\n- Connects to the board's **P1 connector** (the 1x4 header) as a software-I2C bus on SDA = GPIO 48 and SCL = GPIO 47, at 10 kHz, address 0x5F. Probed for \n…",
      "notesHtml": "<p>Feature release building on v0.5, adding a reader-mode web browser over the ESP32-C6, a Notes app, EPUB book support in the reader, a Cyrillic keyboard layout, optional M5Stack CardKB hardware-keyboard support, and a portrait/landscape orientation toggle, alongside several bug fixes. Firmware identity is now 0.6.</p>\n<h2>Web Reader</h2>\n<p>A lightweight reader-mode web browser on the new <strong>Web</strong> home tile, running over the onboard ESP32-C6. It drives the C6 directly over ESP-AT, building its HTTP and TLS requests by hand, so it shares the WiFi companion's C6 link but uses its own request path. The C6 must be connected to WiFi for it to fetch anything.</p>\n<ul>\n<li>Landing menu with <strong>Enter URL</strong>, a <strong>DuckDuckGo Lite</strong> search, <strong>Bookmarks</strong>, and <strong>History</strong>.</li>\n<li>Reader view: pages are parsed to readable text with a status line. Headings render in dark red; link markers show as a muted grey <code>[N]</code> and are followed from the <strong>Links</strong> panel.</li>\n<li>HTTPS support, confirmed against DuckDuckGo Lite.</li>\n<li>GET form fill: an orange <strong>Forms (N)</strong> button opens a picker (duplicate forms collapsed), then a fill modal with a labelled field per text or password input. Submitting builds the <code>action?name=value&amp;...</code> query and loads it through the normal page path, so history and status stay correct.</li>\n<li>Bookmarks (added from the reader view with a confirmation toast) and History.</li>\n<li>This is a <strong>work in progress</strong>: an amber notice appears for a few seconds each time the Web tile is opened. See Known Limitations below and the README for the full list.</li>\n</ul>\n<h2>EPUB Reading</h2>\n<p>The reader now opens <strong>EPUB (<code>.epub</code>)</strong> files in addition to plain text. The file list shows folders, <code>.txt</code>, and <code>.epub</code> items.</p>\n<ul>\n<li>Tapping an EPUB converts it to plain text the first time it is opened. A <em>Converting ... to txt</em> screen is shown while the book is decoded.</li>\n<li>The result is cached as a <code>.txt</code> in a hidden <code>.epub_cache</code> subfolder, so re-opening the same EPUB later loads straight from the cache and the conversion only runs once.</li>\n<li>You only ever see and tap the <code>.epub</code> itself; the cached text stays hidden from the browser.</li>\n</ul>\n<h2>Notes</h2>\n<p>A Notes app on the <strong>Notes</strong> home tile. Notes are plain UTF-8 <code>.txt</code> files stored under <code>/sdcard/notes</code>, so they live on the removable card, survive a reflash, and can be read or edited on a computer. The folder is created automatically on first run.</p>\n<ul>\n<li>Browser modelled on the reader's: folder navigation, an up-one-level button, and a breadcrumb, rooted at <code>/sdcard/notes</code>. A green <strong>+ New Note</strong> row sits at the top of every folder.</li>\n<li>Create and edit with the on-screen keyboard (which honours the KB Theme and KB Layout settings). New notes are named by date and time when the clock is synced (<code>note_YYYYMMDD_HHMM.txt</code>), or sequentially (<code>note_NNN.txt</code>) otherwise.</li>\n<li>Read view reuses the reader's paging: left third of the screen turns back, right two-thirds turns forward, with a progress percentage and a saved-position bookmark.</li>\n<li>Long-press a note for an action menu with <strong>Rename</strong>, <strong>Delete</strong> (with confirmation), and <strong>Cancel</strong>.</li>\n</ul>\n<h2>Cyrillic Keyboard</h2>\n<p>A fourth on-screen keyboard layout, <strong>ЙЦУКЕН</strong> (Cyrillic), added to the <strong>Settings &gt; KB Layout</strong> cycle alongside QWERTY, AZERTY, and QWERTZ.</p>\n<ul>\n<li>Standard Russian arrangement, also commonly used by Bulgarian users. The top three rows carry twelve letters each, with ё on the third row.</li>\n<li>The control keys keep their Latin labels (ABC / abc / 1#) so case and number switching still work.</li>\n<li>Ukrainian and Serbian letter sets are not included. The Settings row shows \"ЙЦУКЕН\" when this layout is active.</li>\n</ul>\n<h2>Physical Keyboard (M5Stack CardKB)</h2>\n<p>Optional support for the <strong>M5Stack CardKB</strong>, a small I2C QWERTY keyboard, as an alternative to the on-screen keyboard for typing messages. Gated behind the <code>MECK_CARDKB</code> build flag (a build-from-source option).</p>\n<ul>\n<li>Connects to the board's <strong>P1 connector</strong> (the 1x4 header) as a software-I2C bus on SDA = GPIO 48 and SCL = GPIO 47, at 10 kHz, address 0x5F. Probed for \n…</li>\n</ul>\n"
    },
    {
      "version": "v0.5",
      "name": "Meck P4 v0.5 Pre-release: Text reader, inline emoji, and repeater admin",
      "datetime": "2026-05-29T09:24:01Z",
      "url": "https://github.com/pelgraine/Meck-P4/releases/tag/v0.5",
      "prerelease": true,
      "notes": "Feature release building on v0.4, adding a plain-text reader, inline colour emoji with a picker, over-the-mesh repeater administration from the companion app, audio player fixes, and draft message saving. Firmware identity is now 0.5.\r\n\r\n## Text Reader\r\n\r\n- New plain-text (.txt) reader, opened from the home grid's Reader tile. Folder browser rooted at `/sdcard/books` with subfolder navigation, a breadcrumb, and an up-one-level button.\r\n- Windowed, page-at-a-time rendering: the reader never loads the whole book, so memory and layout cost stay fixed to one screen regardless of file size. LVGL's own layout finds each page boundary at a clean line break.\r\n- Per-book resume across reboots via a `.pos` sidecar, plus page-back through the pages visited in the current session.\r\n- Percentage progress through the file.\r\n- Reading-view taps: the left third turns back a page, the right two-thirds turns forward. Back returns to the browser; the browser's Back goes up a folder, or home at the `/sdcard/books` root.\r\n- Honours the Settings font-size preference.\r\n\r\n## Inline Emoji\r\n\r\n\r\n<img width=\"270\" height=\"550\" alt=\"IMG_3256\" src=\"https://github.com/user-attachments/assets/0f1d6060-ec01-4973-bad8-0f28f027e80a\" />\r\n<img width=\"270\" height=\"550\" alt=\"IMG_3255\" src=\"https://github.com/user-attachments/assets/b62b299d-8f70-4d18-b8e4-4887b4c5efae\" />\r\n\r\n- Inline colour emoji across the UI. A per-size LVGL image-font (`lv_imgfont`) is attached as the fallback of each `meck_montserrat_*` font, so emoji codepoints the text font can't draw are rendered from baked-in Twemoji images instead of a missing-glyph box. Requires `CONFIG_LV_USE_IMGFONT=y`.\r\n- Emoji picker: a modal scrollable grid opened from the emoji key on the message-composer keyboards, inserting the chosen emoji as UTF-8. Dark-theme aware.\r\n- Expanded emoji set.\r\n- The emoji key moved to the right side of the space bar on the composer keyboards.\r\n- VS-16 presentation selector and the AU-flag regional indicators are handled so trailing combiners don't render as boxes.\r\n\r\n## Repeater Admin (Companion App)\r\n\r\nOver-the-mesh repeater administration from the MeshCore app. The app's session state is kept separate from the on-device Repeater Admin screen, so the two can't capture each other's responses.\r\n\r\n- **CMD_SEND_LOGIN (26)**: log in to a repeater from the app (force-flood for the login itself, `out_path` restored afterwards, `sync_since` reset for room contacts).\r\n- **CMD_SEND_STATUS_REQ (27)**: request a repeater's status block; matched back to the app by tag.\r\n- **CMD_HAS_CONNECTION (28)**: query whether a keep-alive session is active for a contact.\r\n- **CMD_LOGOUT (29)**: end the keep-alive session for a contact.\r\n- **CMD_SEND_BINARY_REQ (50)**: generic binary request (GET_NEIGHBOURS, ACL queries, owner info, and so on), forwarded verbatim and matched by tag. This is the path behind the app's neighbours command.\r\n- **Push notifications**: login success (0x85), login fail (0x86), status response (0x87), and binary response (0x8C). CLI text replies are queued to the app through the existing offline-message path (`TXT_TYPE_CLI_DATA`). Frame layouts mirror upstream byte-for-byte, so the app parses them with no protocol-version negotiation.\r\n- Pushes are delivered to both the BLE and WiFi companions.\r\n\r\n## Audio Player\r\n\r\n- **Track-skip fix**: fixed a cascade where an entire album would skip through in under a second. The end-of-track flag was being set on every player IDLE event, including the internal stop that fires when advancing to the next track, so each advance immediately triggered another. The flag is now set only on a genuine track completion.\r\n- **Bookmarks scoped to audiobooks**: resume bookmarks are kept only for files under `/sdcard/audio/audiobooks`. Music and other audio always start from the beginning, so finishing a short track no longer leaves a near-end position that made it skip on reopen, and no stray bookmark files are written for music.\r\n- **Tappable now-playing ind\n…",
      "notesHtml": "<p>Feature release building on v0.4, adding a plain-text reader, inline colour emoji with a picker, over-the-mesh repeater administration from the companion app, audio player fixes, and draft message saving. Firmware identity is now 0.5.</p>\n<h2>Text Reader</h2>\n<ul>\n<li>New plain-text (.txt) reader, opened from the home grid's Reader tile. Folder browser rooted at <code>/sdcard/books</code> with subfolder navigation, a breadcrumb, and an up-one-level button.</li>\n<li>Windowed, page-at-a-time rendering: the reader never loads the whole book, so memory and layout cost stay fixed to one screen regardless of file size. LVGL's own layout finds each page boundary at a clean line break.</li>\n<li>Per-book resume across reboots via a <code>.pos</code> sidecar, plus page-back through the pages visited in the current session.</li>\n<li>Percentage progress through the file.</li>\n<li>Reading-view taps: the left third turns back a page, the right two-thirds turns forward. Back returns to the browser; the browser's Back goes up a folder, or home at the <code>/sdcard/books</code> root.</li>\n<li>Honours the Settings font-size preference.</li>\n</ul>\n<h2>Inline Emoji</h2>\n<img alt=\"IMG_3256\" src=\"https://github.com/user-attachments/assets/0f1d6060-ec01-4973-bad8-0f28f027e80a\" />\n<img alt=\"IMG_3255\" src=\"https://github.com/user-attachments/assets/b62b299d-8f70-4d18-b8e4-4887b4c5efae\" /><ul>\n<li>Inline colour emoji across the UI. A per-size LVGL image-font (<code>lv_imgfont</code>) is attached as the fallback of each <code>meck_montserrat_*</code> font, so emoji codepoints the text font can't draw are rendered from baked-in Twemoji images instead of a missing-glyph box. Requires <code>CONFIG_LV_USE_IMGFONT=y</code>.</li>\n<li>Emoji picker: a modal scrollable grid opened from the emoji key on the message-composer keyboards, inserting the chosen emoji as UTF-8. Dark-theme aware.</li>\n<li>Expanded emoji set.</li>\n<li>The emoji key moved to the right side of the space bar on the composer keyboards.</li>\n<li>VS-16 presentation selector and the AU-flag regional indicators are handled so trailing combiners don't render as boxes.</li>\n</ul>\n<h2>Repeater Admin (Companion App)</h2>\n<p>Over-the-mesh repeater administration from the MeshCore app. The app's session state is kept separate from the on-device Repeater Admin screen, so the two can't capture each other's responses.</p>\n<ul>\n<li><strong>CMD_SEND_LOGIN (26)</strong>: log in to a repeater from the app (force-flood for the login itself, <code>out_path</code> restored afterwards, <code>sync_since</code> reset for room contacts).</li>\n<li><strong>CMD_SEND_STATUS_REQ (27)</strong>: request a repeater's status block; matched back to the app by tag.</li>\n<li><strong>CMD_HAS_CONNECTION (28)</strong>: query whether a keep-alive session is active for a contact.</li>\n<li><strong>CMD_LOGOUT (29)</strong>: end the keep-alive session for a contact.</li>\n<li><strong>CMD_SEND_BINARY_REQ (50)</strong>: generic binary request (GET_NEIGHBOURS, ACL queries, owner info, and so on), forwarded verbatim and matched by tag. This is the path behind the app's neighbours command.</li>\n<li><strong>Push notifications</strong>: login success (0x85), login fail (0x86), status response (0x87), and binary response (0x8C). CLI text replies are queued to the app through the existing offline-message path (<code>TXT_TYPE_CLI_DATA</code>). Frame layouts mirror upstream byte-for-byte, so the app parses them with no protocol-version negotiation.</li>\n<li>Pushes are delivered to both the BLE and WiFi companions.</li>\n</ul>\n<h2>Audio Player</h2>\n<ul>\n<li><strong>Track-skip fix</strong>: fixed a cascade where an entire album would skip through in under a second. The end-of-track flag was being set on every player IDLE event, including the internal stop that fires when advancing to the next track, so each advance immediately triggered another. The flag is now set only on a genuine track completion.</li>\n<li><strong>Bookmarks scoped to audiobooks</strong>: resume bookmarks are kept only for files under <code>/sdcard/audio/audiobooks</code>. Music and other audio always start from the beginning, so finishing a short track no longer leaves a near-end position that made it skip on reopen, and no stray bookmark files are written for music.</li>\n<li>**Tappable now-playing ind\n…</li>\n</ul>\n"
    },
    {
      "version": "v0.4",
      "name": "Meck P4 v0.4 Pre-release: WiFi companion app support",
      "datetime": "2026-05-28T08:09:22Z",
      "url": "https://github.com/pelgraine/Meck-P4/releases/tag/v0.4",
      "prerelease": true,
      "notes": "Feature release building on v0.3.8, adding WiFi companion app support, companion protocol compatibility, power optimisation, and stability improvements.\r\n\r\n## WiFi Companion Support\r\n\r\n- Full WiFi companion transport via ESP32-C6 AT commands over SDIO. Connect the MeshCore app to your T-Display P4 over your local WiFi network (TCP port 5000).\r\n- WiFi SSID and password configuration from the device Settings screen.\r\n- WiFi IP address displayed on the home screen and settings page with live refresh.\r\n- WiFi-aware sleep: when WiFi is active, the display dims to zero brightness instead of entering light sleep (which would kill the SDIO bus and TCP connection). Touch or boot button wakes the screen.\r\n\r\n## Companion Protocol\r\n\r\nExtensive companion protocol implementation for MeshCore app compatibility:\r\n\r\n- **CMD_DEVICE_QUERY (22)**: Full device info response including firmware version, build date, model, path hash mode.\r\n- **CMD_APP_START (1)**: Identity, radio params, and position shared with the app on connection.\r\n- **CMD_GET_CONTACTS (4)**: Full contact iteration with streaming and incremental sync (since timestamp).\r\n- **CMD_SEND_CHANNEL_TXT_MSG (3)**: Send channel messages from the app, with own-echo detection so the app shows \"Heard X Repeats\".\r\n- **CMD_SET_ADVERT_NAME (8)**: Set node name from the app.\r\n- **CMD_SET_RADIO_PARAMS (11)**: Set frequency, bandwidth, spreading factor, coding rate (reboot to apply).\r\n- **CMD_SET_RADIO_TX_POWER (12)**: Set TX power.\r\n- **CMD_SET_ADVERT_LATLON (14)**: Set position for adverts.\r\n- **CMD_REMOVE_CONTACT (15)**: Delete contacts from the app.\r\n- **CMD_EXPORT_PRIVATE_KEY (23)**: Export identity for config backup.\r\n- **CMD_IMPORT_PRIVATE_KEY (24)**: Import identity for config restore.\r\n- **CMD_GET_CONTACT_BY_KEY (30)**: Look up contact by public key (required for app-side delete).\r\n- **CMD_SET_CHANNEL (32)**: Import/update channel name and secret from the app.\r\n- **CMD_SET_OTHER_PARAMS (38)**: Set manual add, advert location policy, multi-acks.\r\n- **CMD_GET_CUSTOM_VARS (40)**: Returns GPS state, interval, latitude, longitude for the app's Position Settings page.\r\n- **CMD_SET_CUSTOM_VAR (41)**: Toggle GPS on/off, set latitude/longitude from the app.\r\n- **CMD_GET_ADVERT_PATH (42)**: Returns cached advert path hashes so the app can display route details with repeater names.\r\n- **CMD_SET_FLOOD_SCOPE_KEY (54)**: Acknowledged (stub).\r\n- **CMD_SET_PATH_HASH_MODE (61)**: Set path hash size (1-byte, 2-byte, or 3-byte) from the app's Experimental Settings.\r\n- **CMD_SET_DEFAULT_FLOOD_SCOPE (63)**: Set/clear default region scope from the app.\r\n- **CMD_GET_DEFAULT_FLOOD_SCOPE (64)**: Returns current scope name and key.\r\n- **Push notifications**: Channel messages, DM messages, send confirmations (acks), new adverts, and path updates are all pushed to the app in real time.\r\n\r\n## Channel Expansion\r\n\r\n- Channel support expanded from 12 to 40 (`MAX_GROUP_CHANNELS`).\r\n- Channel notification preferences array expanded to match (41 slots: 40 channels + 1 DM).\r\n- Channel picker UI updated to support all 40 channels.\r\n- Stack-local channel record arrays moved to heap allocation to prevent stack overflow.\r\n\r\n## Power Optimisation\r\n\r\n- **ICM20948 IMU**: Put to sleep after init (~3mA saved).\r\n- **ES8311 audio codec**: DAC, ADC, and PGA powered down after init. I2S channels disabled to release APB_FREQ_MAX PM locks. Woken on demand before playback, slept after stop (~5-10mA saved).\r\n- **I2S PM lock management**: Added `disable_channels()` / `enable_channels()` to Hardware_Iis class so I2S driver PM locks are only held during active audio playback.\r\n- **DFS investigation**: PM stats task added (enable via sdkconfig). Confirmed DSI display driver holds CPU_FREQ_MAX lock 97% of the time while screen is on, making CPU frequency reduction ineffective. ~172mA baseline is the hardware floor with display active.\r\n\r\n## UI Improvements\r\n\r\n- **Path display**: Incoming message routes and outgoing heard-by lists reformatted as n\n…",
      "notesHtml": "<p>Feature release building on v0.3.8, adding WiFi companion app support, companion protocol compatibility, power optimisation, and stability improvements.</p>\n<h2>WiFi Companion Support</h2>\n<ul>\n<li>Full WiFi companion transport via ESP32-C6 AT commands over SDIO. Connect the MeshCore app to your T-Display P4 over your local WiFi network (TCP port 5000).</li>\n<li>WiFi SSID and password configuration from the device Settings screen.</li>\n<li>WiFi IP address displayed on the home screen and settings page with live refresh.</li>\n<li>WiFi-aware sleep: when WiFi is active, the display dims to zero brightness instead of entering light sleep (which would kill the SDIO bus and TCP connection). Touch or boot button wakes the screen.</li>\n</ul>\n<h2>Companion Protocol</h2>\n<p>Extensive companion protocol implementation for MeshCore app compatibility:</p>\n<ul>\n<li><strong>CMD_DEVICE_QUERY (22)</strong>: Full device info response including firmware version, build date, model, path hash mode.</li>\n<li><strong>CMD_APP_START (1)</strong>: Identity, radio params, and position shared with the app on connection.</li>\n<li><strong>CMD_GET_CONTACTS (4)</strong>: Full contact iteration with streaming and incremental sync (since timestamp).</li>\n<li><strong>CMD_SEND_CHANNEL_TXT_MSG (3)</strong>: Send channel messages from the app, with own-echo detection so the app shows \"Heard X Repeats\".</li>\n<li><strong>CMD_SET_ADVERT_NAME (8)</strong>: Set node name from the app.</li>\n<li><strong>CMD_SET_RADIO_PARAMS (11)</strong>: Set frequency, bandwidth, spreading factor, coding rate (reboot to apply).</li>\n<li><strong>CMD_SET_RADIO_TX_POWER (12)</strong>: Set TX power.</li>\n<li><strong>CMD_SET_ADVERT_LATLON (14)</strong>: Set position for adverts.</li>\n<li><strong>CMD_REMOVE_CONTACT (15)</strong>: Delete contacts from the app.</li>\n<li><strong>CMD_EXPORT_PRIVATE_KEY (23)</strong>: Export identity for config backup.</li>\n<li><strong>CMD_IMPORT_PRIVATE_KEY (24)</strong>: Import identity for config restore.</li>\n<li><strong>CMD_GET_CONTACT_BY_KEY (30)</strong>: Look up contact by public key (required for app-side delete).</li>\n<li><strong>CMD_SET_CHANNEL (32)</strong>: Import/update channel name and secret from the app.</li>\n<li><strong>CMD_SET_OTHER_PARAMS (38)</strong>: Set manual add, advert location policy, multi-acks.</li>\n<li><strong>CMD_GET_CUSTOM_VARS (40)</strong>: Returns GPS state, interval, latitude, longitude for the app's Position Settings page.</li>\n<li><strong>CMD_SET_CUSTOM_VAR (41)</strong>: Toggle GPS on/off, set latitude/longitude from the app.</li>\n<li><strong>CMD_GET_ADVERT_PATH (42)</strong>: Returns cached advert path hashes so the app can display route details with repeater names.</li>\n<li><strong>CMD_SET_FLOOD_SCOPE_KEY (54)</strong>: Acknowledged (stub).</li>\n<li><strong>CMD_SET_PATH_HASH_MODE (61)</strong>: Set path hash size (1-byte, 2-byte, or 3-byte) from the app's Experimental Settings.</li>\n<li><strong>CMD_SET_DEFAULT_FLOOD_SCOPE (63)</strong>: Set/clear default region scope from the app.</li>\n<li><strong>CMD_GET_DEFAULT_FLOOD_SCOPE (64)</strong>: Returns current scope name and key.</li>\n<li><strong>Push notifications</strong>: Channel messages, DM messages, send confirmations (acks), new adverts, and path updates are all pushed to the app in real time.</li>\n</ul>\n<h2>Channel Expansion</h2>\n<ul>\n<li>Channel support expanded from 12 to 40 (<code>MAX_GROUP_CHANNELS</code>).</li>\n<li>Channel notification preferences array expanded to match (41 slots: 40 channels + 1 DM).</li>\n<li>Channel picker UI updated to support all 40 channels.</li>\n<li>Stack-local channel record arrays moved to heap allocation to prevent stack overflow.</li>\n</ul>\n<h2>Power Optimisation</h2>\n<ul>\n<li><strong>ICM20948 IMU</strong>: Put to sleep after init (~3mA saved).</li>\n<li><strong>ES8311 audio codec</strong>: DAC, ADC, and PGA powered down after init. I2S channels disabled to release APB_FREQ_MAX PM locks. Woken on demand before playback, slept after stop (~5-10mA saved).</li>\n<li><strong>I2S PM lock management</strong>: Added <code>disable_channels()</code> / <code>enable_channels()</code> to Hardware_Iis class so I2S driver PM locks are only held during active audio playback.</li>\n<li><strong>DFS investigation</strong>: PM stats task added (enable via sdkconfig). Confirmed DSI display driver holds CPU_FREQ_MAX lock 97% of the time while screen is on, making CPU frequency reduction ineffective. ~172mA baseline is the hardware floor with display active.</li>\n</ul>\n<h2>UI Improvements</h2>\n<ul>\n<li><strong>Path display</strong>: Incoming message routes and outgoing heard-by lists reformatted as n\n…</li>\n</ul>\n"
    },
    {
      "version": "v0.3.8",
      "name": "v0.3.8",
      "datetime": "2026-05-27T02:36:17Z",
      "url": "https://github.com/pelgraine/Meck-P4/releases/tag/v0.3.8",
      "prerelease": true,
      "notes": "Pre-release.\r\n\r\nFeature release building on v0.3.6, adding position adverts, share position, path view, private channels, and voice/picture over LoRa infrastructure (disabled pending final integration).\r\n\r\n---\r\n\r\n## What's New\r\n\r\n### Position Adverts\r\n\r\nYour GPS position can now be encoded into outgoing adverts, allowing other nodes on the mesh to see your location on their Maps screen. Position is configured via a new **Settings > Position** sub-screen with three fields:\r\n\r\n- **Latitude / Longitude** (tap to edit) for manual entry in decimal degrees\r\n- **Share Position** (tap to cycle): Off / Manual / Auto-GPS\r\n\r\nIn Auto-GPS mode, the L76K GPS snapshot is polled every 15 minutes and the stored position is updated automatically whenever the fix changes. A **Copy Position** button is also available on the sub-screen.\r\n\r\n### Share Position\r\n\r\nThe **+** button on the channel messages and DM compose screens now includes a **Share Position** option. Tapping it sends your current lat/lon as a message to the active channel or conversation.\r\n\r\n### Path View and Message Actions\r\n\r\nLong-pressing an incoming message now opens **Path View**, which displays the routing path that message took through the mesh, showing each hop in the route.\r\n\r\nLong-pressing an outgoing message gives you the option to **Retry Send** if the message failed, or to see which repeaters acknowledged the message.\r\n\r\n### Private Channels\r\n\r\nChannels can now be public or private:\r\n\r\n- **Public channels** start with `#` (e.g. `#test`). The secret is derived via SHA-256 from the name, matching the standard MeshCore convention.\r\n- **Private channels** have no `#` prefix. A random 16-byte secret is generated at creation, so only invited users can join.\r\n\r\n**Sharing:** open **Settings > Channels**, tap the channel, then tap **Share Channel**. This opens a contact picker and sends the channel name and secret as a DM invitation. Recipients see a pending invite they can accept (tap) or dismiss (long-press).\r\n\r\n---\r\n\r\n### Voice over LoRa Infrastructure (Not Yet Enabled)\r\n\r\nThe full infrastructure for Codec2 1200bps voice messaging has been added to the firmware:\r\n\r\n- Codec2 ESP-IDF component (`components/codec2/`) wrapping drowe67/codec2 with only the core 1200bps mode compiled in\r\n- ES8311 microphone capture at native 44100 Hz I2S rate\r\n- VE3 protocol for chunked voice packet transfer\r\n- Recording buffer in PSRAM, staggered send timing\r\n- Voice UI: inbox with metadata persistence, record screen with Play/Discard/Send, previous recordings list, contact picker (favourites first), send status tracking\r\n- Playback at 85% volume via meck_audio_play_file\r\n\r\nVoice and Picture tiles are present on the home screen as placeholders but the features are disabled in this release pending final integration and testing.\r\n\r\n### Picture over LoRa Infrastructure (Not Yet Enabled)\r\n\r\nChunked image transfer protocol infrastructure has been added but is disabled in this release.\r\n\r\n### UI Changes\r\n\r\n- **Splash screen** updated with new design and progress bar\r\n- **Maps and Trace tile colours** swapped so there are no longer two adjacent red tiles on the home screen\r\n- **Home screen** expanded from seven tiles to nine (Voice and Camera placeholders added)\r\n- **MAX_GROUP_CHANNELS** bumped from 8 to 12\r\n\r\n---\r\n\r\n## First-Time Flashing: Read This First\r\n\r\nMeck-P4 ships as a single **merged binary** (bootloader + partition table + application combined). One file, flash at offset `0x0`.\r\n> **An SD card is recommended.** With a FAT32-formatted card inserted, every saved setting, channel message, DM, and room post is mirrored automatically, notification tones have somewhere to live, and the device recovers gracefully from a wiped NVS. Without an SD card the device still works but loses message history on reboot and notification sounds won't be available.\r\n\r\nEnsure you use the **right-side USB-C port** (the data port), not the high-speed charger port, to flash.\r\n<img width=\"377\" height=\"284\" alt=\"correct \n…",
      "notesHtml": "<p>Pre-release.</p>\n<p>Feature release building on v0.3.6, adding position adverts, share position, path view, private channels, and voice/picture over LoRa infrastructure (disabled pending final integration).</p>\n<hr />\n<h2>What's New</h2>\n<h3>Position Adverts</h3>\n<p>Your GPS position can now be encoded into outgoing adverts, allowing other nodes on the mesh to see your location on their Maps screen. Position is configured via a new <strong>Settings &gt; Position</strong> sub-screen with three fields:</p>\n<ul>\n<li><strong>Latitude / Longitude</strong> (tap to edit) for manual entry in decimal degrees</li>\n<li><strong>Share Position</strong> (tap to cycle): Off / Manual / Auto-GPS</li>\n</ul>\n<p>In Auto-GPS mode, the L76K GPS snapshot is polled every 15 minutes and the stored position is updated automatically whenever the fix changes. A <strong>Copy Position</strong> button is also available on the sub-screen.</p>\n<h3>Share Position</h3>\n<p>The <strong>+</strong> button on the channel messages and DM compose screens now includes a <strong>Share Position</strong> option. Tapping it sends your current lat/lon as a message to the active channel or conversation.</p>\n<h3>Path View and Message Actions</h3>\n<p>Long-pressing an incoming message now opens <strong>Path View</strong>, which displays the routing path that message took through the mesh, showing each hop in the route.</p>\n<p>Long-pressing an outgoing message gives you the option to <strong>Retry Send</strong> if the message failed, or to see which repeaters acknowledged the message.</p>\n<h3>Private Channels</h3>\n<p>Channels can now be public or private:</p>\n<ul>\n<li><strong>Public channels</strong> start with <code>#</code> (e.g. <code>#test</code>). The secret is derived via SHA-256 from the name, matching the standard MeshCore convention.</li>\n<li><strong>Private channels</strong> have no <code>#</code> prefix. A random 16-byte secret is generated at creation, so only invited users can join.</li>\n</ul>\n<p><strong>Sharing:</strong> open <strong>Settings &gt; Channels</strong>, tap the channel, then tap <strong>Share Channel</strong>. This opens a contact picker and sends the channel name and secret as a DM invitation. Recipients see a pending invite they can accept (tap) or dismiss (long-press).</p>\n<hr />\n<h3>Voice over LoRa Infrastructure (Not Yet Enabled)</h3>\n<p>The full infrastructure for Codec2 1200bps voice messaging has been added to the firmware:</p>\n<ul>\n<li>Codec2 ESP-IDF component (<code>components/codec2/</code>) wrapping drowe67/codec2 with only the core 1200bps mode compiled in</li>\n<li>ES8311 microphone capture at native 44100 Hz I2S rate</li>\n<li>VE3 protocol for chunked voice packet transfer</li>\n<li>Recording buffer in PSRAM, staggered send timing</li>\n<li>Voice UI: inbox with metadata persistence, record screen with Play/Discard/Send, previous recordings list, contact picker (favourites first), send status tracking</li>\n<li>Playback at 85% volume via meck_audio_play_file</li>\n</ul>\n<p>Voice and Picture tiles are present on the home screen as placeholders but the features are disabled in this release pending final integration and testing.</p>\n<h3>Picture over LoRa Infrastructure (Not Yet Enabled)</h3>\n<p>Chunked image transfer protocol infrastructure has been added but is disabled in this release.</p>\n<h3>UI Changes</h3>\n<ul>\n<li><strong>Splash screen</strong> updated with new design and progress bar</li>\n<li><strong>Maps and Trace tile colours</strong> swapped so there are no longer two adjacent red tiles on the home screen</li>\n<li><strong>Home screen</strong> expanded from seven tiles to nine (Voice and Camera placeholders added)</li>\n<li><strong>MAX_GROUP_CHANNELS</strong> bumped from 8 to 12</li>\n</ul>\n<hr />\n<h2>First-Time Flashing: Read This First</h2>\n<p>Meck-P4 ships as a single <strong>merged binary</strong> (bootloader + partition table + application combined). One file, flash at offset <code>0x0</code>.</p>\n<blockquote>\n<p><strong>An SD card is recommended.</strong> With a FAT32-formatted card inserted, every saved setting, channel message, DM, and room post is mirrored automatically, notification tones have somewhere to live, and the device recovers gracefully from a wiped NVS. Without an SD card the device still works but loses message history on reboot and notification sounds won't be available.</p>\n</blockquote>\n<p>Ensure you use the <strong>right-side USB-C port</strong> (the data port), not the high-speed charger port, to flash.\n&lt;img width=\"377\" height=\"284\" alt=\"correct \n…</p>\n"
    },
    {
      "version": "v0.3.6",
      "name": "Meck-P4 v0.3.6: Custom Radio, Region Scope, Channels, Notification Sounds",
      "datetime": "2026-05-24T23:38:02Z",
      "url": "https://github.com/pelgraine/Meck-P4/releases/tag/v0.3.6",
      "prerelease": true,
      "notes": "Pre-release.\r\n\r\nFeature release building on v0.3.5.1, adding custom radio parameters, region scope support, a Channels settings sub-screen, and per-channel notification sounds.\r\n\r\n---\r\n\r\n## What's New\r\n\r\n### Custom Radio Parameters\r\n\r\nFrequency, Bandwidth, and Spreading Factor are now individually editable via text entry in Settings (tap the row, type the value, tap Confirm). Coding Rate cycles on tap (4/5 through 4/8). The Radio Preset row remains available and shows \"Custom\" whenever the current values don't match any of the 17 built-in presets. Selecting a preset populates all four fields; you can then tweak individual values without losing the rest.\r\n\r\nThis is primarily useful for users in regions where the community presets don't cover the exact parameters in use, such as parts of Europe running SR 5/6 with 32.5 kHz bandwidth, or anyone wanting to customise the coding rate independently.\r\n\r\n### Region Scope (MeshCore v1.15+ Compatibility)\r\n\r\nRegions limit how far your flood messages propagate through the mesh. When you set a region, outgoing messages are tagged with a transport code that repeaters use to decide whether to forward them. Messages sent without a region reach all repeaters via the default wildcard, same as always.\r\n\r\n**Setting your device region:**\r\n- Open **Settings**, tap **Default Region**, enter your region name (e.g. `au-nsw`), tap **Confirm**\r\n- Leave empty for unscoped (legacy behaviour, reaches all repeaters)\r\n\r\n**Per-channel region:** Open **Settings > Channels**, tap a channel, then tap **Region Scope** to set an override for that specific channel. Empty uses the device default.\r\n\r\n**Finding your region name:** Region names are determined by your local mesh community. Check with your local group, or browse the community region registry at [regions.meshcore.nz](https://regions.meshcore.nz/) for names already in use. Common patterns follow ISO 3166 codes (e.g. `au` for Australia, `au-nsw`, `gb-eng`, `us-ca`), but communities may also use custom names. Names must be lowercase alphanumeric characters and hyphens, max 30 characters. You can also create your own custom regions on repeaters you manage (see Repeater Admin below).\r\n\r\n**Note:** Region scoping requires repeaters in your area to be running MeshCore v1.10+ with the appropriate regions configured. If no repeaters have region filtering enabled, scoped and unscoped messages behave identically.\r\n\r\n### Repeater Admin: Region Management via CLI\r\n\r\nRepeaters running MeshCore v1.10+ support full region management through the Cmd Line screen in Repeater Admin. Log in to a repeater, tap **Cmd Line**, and use the following commands:\r\n\r\n| Command | Description |\r\n| --- | --- |\r\n| `region put <name> [parent]` | Create a new region (optional parent for nesting) |\r\n| `region remove <name>` | Remove a region (remove children first) |\r\n| `region allowf <name>` | Allow flooding for a region |\r\n| `region denyf <name>` | Block flooding for a region |\r\n| `region get <name>` | Show info for a region |\r\n| `region home` / `region home <name>` | View or set the repeater's home region |\r\n| `region default` / `region default <name>` | View or set the repeater's default scope |\r\n| `region save` | Persist region changes to the repeater's flash |\r\n| `region list allowed` / `region list denied` | View regions (repeater firmware 1.12+) |\r\n| `region load <name> [F]` | Single-line region load (append `F` to allow flooding) |\r\n\r\n**Not supported:** The interactive multi-line `region load` (without parameters) requires a serial terminal and is not available on the P4, which does not currently support serial CLI commands. Use individual `region put` and `region allowf` commands instead.\r\n\r\n**Quick start example** (setting up a repeater for the `au-nsw` region):\r\n```\r\nregion put au-nsw\r\nregion allowf au-nsw\r\nregion home au-nsw\r\nregion save\r\n```\r\n\r\nFor full documentation and nested region examples, see the [MeshCore CLI docs](https://docs.meshcore.io/cli_commands/#region-management-v1\n…",
      "notesHtml": "<p>Pre-release.</p>\n<p>Feature release building on v0.3.5.1, adding custom radio parameters, region scope support, a Channels settings sub-screen, and per-channel notification sounds.</p>\n<hr />\n<h2>What's New</h2>\n<h3>Custom Radio Parameters</h3>\n<p>Frequency, Bandwidth, and Spreading Factor are now individually editable via text entry in Settings (tap the row, type the value, tap Confirm). Coding Rate cycles on tap (4/5 through 4/8). The Radio Preset row remains available and shows \"Custom\" whenever the current values don't match any of the 17 built-in presets. Selecting a preset populates all four fields; you can then tweak individual values without losing the rest.</p>\n<p>This is primarily useful for users in regions where the community presets don't cover the exact parameters in use, such as parts of Europe running SR 5/6 with 32.5 kHz bandwidth, or anyone wanting to customise the coding rate independently.</p>\n<h3>Region Scope (MeshCore v1.15+ Compatibility)</h3>\n<p>Regions limit how far your flood messages propagate through the mesh. When you set a region, outgoing messages are tagged with a transport code that repeaters use to decide whether to forward them. Messages sent without a region reach all repeaters via the default wildcard, same as always.</p>\n<p><strong>Setting your device region:</strong></p>\n<ul>\n<li>Open <strong>Settings</strong>, tap <strong>Default Region</strong>, enter your region name (e.g. <code>au-nsw</code>), tap <strong>Confirm</strong></li>\n<li>Leave empty for unscoped (legacy behaviour, reaches all repeaters)</li>\n</ul>\n<p><strong>Per-channel region:</strong> Open <strong>Settings &gt; Channels</strong>, tap a channel, then tap <strong>Region Scope</strong> to set an override for that specific channel. Empty uses the device default.</p>\n<p><strong>Finding your region name:</strong> Region names are determined by your local mesh community. Check with your local group, or browse the community region registry at <a href=\"https://regions.meshcore.nz/\" target=\"_blank\" rel=\"noopener noreferrer\">regions.meshcore.nz</a> for names already in use. Common patterns follow ISO 3166 codes (e.g. <code>au</code> for Australia, <code>au-nsw</code>, <code>gb-eng</code>, <code>us-ca</code>), but communities may also use custom names. Names must be lowercase alphanumeric characters and hyphens, max 30 characters. You can also create your own custom regions on repeaters you manage (see Repeater Admin below).</p>\n<p><strong>Note:</strong> Region scoping requires repeaters in your area to be running MeshCore v1.10+ with the appropriate regions configured. If no repeaters have region filtering enabled, scoped and unscoped messages behave identically.</p>\n<h3>Repeater Admin: Region Management via CLI</h3>\n<p>Repeaters running MeshCore v1.10+ support full region management through the Cmd Line screen in Repeater Admin. Log in to a repeater, tap <strong>Cmd Line</strong>, and use the following commands:</p>\n<table>\n<thead>\n<tr>\n<th>Command</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>region put &lt;name&gt; [parent]</code></td>\n<td>Create a new region (optional parent for nesting)</td>\n</tr>\n<tr>\n<td><code>region remove &lt;name&gt;</code></td>\n<td>Remove a region (remove children first)</td>\n</tr>\n<tr>\n<td><code>region allowf &lt;name&gt;</code></td>\n<td>Allow flooding for a region</td>\n</tr>\n<tr>\n<td><code>region denyf &lt;name&gt;</code></td>\n<td>Block flooding for a region</td>\n</tr>\n<tr>\n<td><code>region get &lt;name&gt;</code></td>\n<td>Show info for a region</td>\n</tr>\n<tr>\n<td><code>region home</code> / <code>region home &lt;name&gt;</code></td>\n<td>View or set the repeater's home region</td>\n</tr>\n<tr>\n<td><code>region default</code> / <code>region default &lt;name&gt;</code></td>\n<td>View or set the repeater's default scope</td>\n</tr>\n<tr>\n<td><code>region save</code></td>\n<td>Persist region changes to the repeater's flash</td>\n</tr>\n<tr>\n<td><code>region list allowed</code> / <code>region list denied</code></td>\n<td>View regions (repeater firmware 1.12+)</td>\n</tr>\n<tr>\n<td><code>region load &lt;name&gt; [F]</code></td>\n<td>Single-line region load (append <code>F</code> to allow flooding)</td>\n</tr>\n</tbody></table>\n<p><strong>Not supported:</strong> The interactive multi-line <code>region load</code> (without parameters) requires a serial terminal and is not available on the P4, which does not currently support serial CLI commands. Use individual <code>region put</code> and <code>region allowf</code> commands instead.</p>\n<p><strong>Quick start example</strong> (setting up a repeater for the <code>au-nsw</code> region):</p>\n<pre><code>region put au-nsw\nregion allowf au-nsw\nregion home au-nsw\nregion save\n</code></pre>\n<p>For full documentation and nested region examples, see the [MeshCore CLI docs](<a href=\"https://docs.meshcore.io/cli_commands/#region-management-v1\" target=\"_blank\" rel=\"noopener noreferrer\">https://docs.meshcore.io/cli_commands/#region-management-v1</a>\n…</p>\n"
    },
    {
      "version": "v0.3.5.1",
      "name": "Meck-P4 v0.3.5.1: Contacts list fixes",
      "datetime": "2026-05-22T19:34:27Z",
      "url": "https://github.com/pelgraine/Meck-P4/releases/tag/v0.3.5.1",
      "prerelease": true,
      "notes": "Pre-release.\r\n\r\nPatch release on top of v0.3.5, fixing three bugs in the Contacts screen:\r\n\r\n- **200-row cap removed.** The contacts list previously stopped rendering after 200 matched rows. With 200+ contacts this hid recently-added repeaters from the list entirely (including, in one reproducible case, a freshly-added Repeater that Discover claimed to have added but which was nowhere to be seen). The list now displays every matching contact.\r\n- **Sorted by recency.** Contacts now appear newest-first instead of in creation order. The sort key is local activity time (advert receipt, message send, or message receive), so the most recently active contacts sit at the top of the list regardless of which filter is active.\r\n- **Sort key corrected.** Initial attempt used `last_advert_timestamp`, which is the *sender's claimed clock* embedded in the advert payload rather than our reception time. That gave nonsense ordering: nodes with forward-dated clocks (stale build epoch, mis-set NTP, etc.) sat permanently at the top, and nodes whose clocks were stuck or behind had their adverts silently dropped by the upstream replay-attack guard and sank to the bottom. The fix sorts by `lastmod` (our local RTC time at last contact activity) with an override in `onAdvertRecv` so `lastmod` is updated on every advert receipt regardless of whether the upstream replay guard early-returned.\r\n\r\nNo other behavioural changes from v0.3.5.\r\n\r\n## First-Time Flashing: Read This First\r\n\r\n(Same as v0.3.5 — skip this section if you've already flashed v0.3.5; the contacts fixes carry over via the usual upgrade path.)\r\n\r\nMeck-P4 ships as a single **merged binary** (bootloader + partition table + application combined). One file, flash at offset `0x0`.\r\n\r\n> **An SD card is recommended.** With a FAT32-formatted card inserted, every saved setting, channel message, DM, and room post is mirrored automatically, audio files have somewhere to live, and the device recovers gracefully from a wiped NVS. Without an SD card the device still works but loses message history on reboot.\r\n\r\nEnsure you use the **right-side USB-C port** (the data port), not the high-speed charger port, to flash.\r\n<img width=\"377\" height=\"284\" alt=\"correct port usage for flashing\" src=\"https://github.com/user-attachments/assets/b6634315-9f7b-4b63-a944-1c52508a101c\" />\r\n\r\n### Flashing with the MeshCore Web Flasher (recommended)\r\n\r\n1. Go to <https://flasher.meshcore.io/>\r\n2. Scroll to the bottom and select **Custom Firmware**\r\n3. Select the `meck-p4-0.3.5.1-version-merged.bin` file you downloaded\r\n4. Click OK on the merged-binary warning\r\n5. Click **Flash**, pick your device in the popup, and click **Connect**\r\n\r\n### Flashing with esptool.py\r\n\r\n```\r\npip install esptool\r\nesptool.py --chip esp32p4 -p PORT write_flash 0x0 meck-p4-0.3.5.1-version-merged.bin\r\n```\r\n\r\n(Replace `PORT` with `/dev/cu.usbmodemXXXX` on macOS, `/dev/ttyACM0` on Linux, or `COM3` on Windows.)\r\n\r\nUpgrading from v0.3.5 or v0.3.3 does not require `erase_flash`. The prefs loader continues to tolerate older shorter-blob layouts, so any new fields come up at default rather than wiping existing settings.\r\n\r\n> ⚠️ **AMOLED variant remains untested by the maintainer.** The `-amoled.bin` is built from the same source tree with the AMOLED display option selected in menuconfig, but the project maintainer doesn't have the AMOLED hardware on hand. The TFT variant is the tested and known-working build.\r\n\r\n## Known Limitations\r\n\r\nCarried over unchanged from v0.3.5:\r\n\r\n- **Background audio playback has known issues — not recommended for general use yet.** A blue `>>` glyph was added to the top bar to indicate when the audio player is actively playing, mirrored across every home tile and full screen. However, the broader background-playback experience has a number of unresolved bugs: tapping the Audio tile while a track is playing doesn't jump back to the Now Playing screen (so pausing requires navigating back to the file manually), and starting a second \n…",
      "notesHtml": "<p>Pre-release.</p>\n<p>Patch release on top of v0.3.5, fixing three bugs in the Contacts screen:</p>\n<ul>\n<li><strong>200-row cap removed.</strong> The contacts list previously stopped rendering after 200 matched rows. With 200+ contacts this hid recently-added repeaters from the list entirely (including, in one reproducible case, a freshly-added Repeater that Discover claimed to have added but which was nowhere to be seen). The list now displays every matching contact.</li>\n<li><strong>Sorted by recency.</strong> Contacts now appear newest-first instead of in creation order. The sort key is local activity time (advert receipt, message send, or message receive), so the most recently active contacts sit at the top of the list regardless of which filter is active.</li>\n<li><strong>Sort key corrected.</strong> Initial attempt used <code>last_advert_timestamp</code>, which is the <em>sender's claimed clock</em> embedded in the advert payload rather than our reception time. That gave nonsense ordering: nodes with forward-dated clocks (stale build epoch, mis-set NTP, etc.) sat permanently at the top, and nodes whose clocks were stuck or behind had their adverts silently dropped by the upstream replay-attack guard and sank to the bottom. The fix sorts by <code>lastmod</code> (our local RTC time at last contact activity) with an override in <code>onAdvertRecv</code> so <code>lastmod</code> is updated on every advert receipt regardless of whether the upstream replay guard early-returned.</li>\n</ul>\n<p>No other behavioural changes from v0.3.5.</p>\n<h2>First-Time Flashing: Read This First</h2>\n<p>(Same as v0.3.5 — skip this section if you've already flashed v0.3.5; the contacts fixes carry over via the usual upgrade path.)</p>\n<p>Meck-P4 ships as a single <strong>merged binary</strong> (bootloader + partition table + application combined). One file, flash at offset <code>0x0</code>.</p>\n<blockquote>\n<p><strong>An SD card is recommended.</strong> With a FAT32-formatted card inserted, every saved setting, channel message, DM, and room post is mirrored automatically, audio files have somewhere to live, and the device recovers gracefully from a wiped NVS. Without an SD card the device still works but loses message history on reboot.</p>\n</blockquote>\n<p>Ensure you use the <strong>right-side USB-C port</strong> (the data port), not the high-speed charger port, to flash.\n<img alt=\"correct port usage for flashing\" src=\"https://github.com/user-attachments/assets/b6634315-9f7b-4b63-a944-1c52508a101c\" /></p>\n<h3>Flashing with the MeshCore Web Flasher (recommended)</h3>\n<ol>\n<li>Go to <a href=\"https://flasher.meshcore.io/\" target=\"_blank\" rel=\"noopener noreferrer\">https://flasher.meshcore.io/</a></li>\n<li>Scroll to the bottom and select <strong>Custom Firmware</strong></li>\n<li>Select the <code>meck-p4-0.3.5.1-version-merged.bin</code> file you downloaded</li>\n<li>Click OK on the merged-binary warning</li>\n<li>Click <strong>Flash</strong>, pick your device in the popup, and click <strong>Connect</strong></li>\n</ol>\n<h3>Flashing with esptool.py</h3>\n<pre><code>pip install esptool\nesptool.py --chip esp32p4 -p PORT write_flash 0x0 meck-p4-0.3.5.1-version-merged.bin\n</code></pre>\n<p>(Replace <code>PORT</code> with <code>/dev/cu.usbmodemXXXX</code> on macOS, <code>/dev/ttyACM0</code> on Linux, or <code>COM3</code> on Windows.)</p>\n<p>Upgrading from v0.3.5 or v0.3.3 does not require <code>erase_flash</code>. The prefs loader continues to tolerate older shorter-blob layouts, so any new fields come up at default rather than wiping existing settings.</p>\n<blockquote>\n<p>⚠️ <strong>AMOLED variant remains untested by the maintainer.</strong> The <code>-amoled.bin</code> is built from the same source tree with the AMOLED display option selected in menuconfig, but the project maintainer doesn't have the AMOLED hardware on hand. The TFT variant is the tested and known-working build.</p>\n</blockquote>\n<h2>Known Limitations</h2>\n<p>Carried over unchanged from v0.3.5:</p>\n<ul>\n<li><strong>Background audio playback has known issues — not recommended for general use yet.</strong> A blue <code>&gt;&gt;</code> glyph was added to the top bar to indicate when the audio player is actively playing, mirrored across every home tile and full screen. However, the broader background-playback experience has a number of unresolved bugs: tapping the Audio tile while a track is playing doesn't jump back to the Now Playing screen (so pausing requires navigating back to the file manually), and starting a second \n…</li>\n</ul>\n"
    },
    {
      "version": "v0.3.5",
      "name": "Meck-P4 v0.3.5: Direct Messaging, Repeater Admin, Room servers & Maps",
      "datetime": "2026-05-21T09:56:27Z",
      "url": "https://github.com/pelgraine/Meck-P4/releases/tag/v0.3.5",
      "prerelease": true,
      "notes": "Direct messaging arrives in v0.3.5 — compose, send, receive, and persist DMs from any companion contact, with unread badges viewable in the channel list. Room server support and repeater admin land alongside, giving you logged-in access to room post history and the full status/CLI/settings interface for remote repeater management. A new map screen renders OSM slippy tiles from the SD card with a GPS dot, contact markers, and pan/zoom. Plus a per-contact path editor, a standalone trace route screen, a keyboard layout overhaul, a GPS time-sync fix, debug logs to SD, and config export to a MeshCore-app-compatible JSON file.\r\n\r\n> ⚠️ **Still a pre-release.** ⚠️ Web browser, IRC, and the BLE/WiFi companion path are still not in this build. See What's Not Yet Implemented for the full pending list.\r\n\r\n## First-Time Flashing: Read This First\r\n\r\nMeck-P4 ships as a single **merged binary** (bootloader + partition table + application combined). One file, flash at offset `0x0`.\r\n\r\n> **An SD card is recommended.** With a FAT32-formatted card inserted, every saved setting, channel message, DM, and room post is mirrored automatically, audio files have somewhere to live, and the device recovers gracefully from a wiped NVS. Without an SD card the device still works but loses message history on reboot.\r\n\r\nEnsure you use the **right-side USB-C port** (the data port), not the high-speed charger port, to flash.\r\n<img width=\"377\" height=\"284\" alt=\"correct port usage for flashing\" src=\"https://github.com/user-attachments/assets/d3a7e17d-5efa-46c3-af5e-61f97f125569\" />\r\n\r\n### Flashing with the MeshCore Web Flasher (recommended)\r\n\r\n1. Go to <https://flasher.meshcore.io/>\r\n2. Scroll to the bottom and select **Custom Firmware**\r\n3. Select the `meck-p4-0.3.5-version-merged.bin` file you downloaded\r\n4. Click OK on the merged-binary warning\r\n5. Click **Flash**, pick your device in the popup, and click **Connect**\r\n\r\n### Flashing with esptool.py\r\n\r\n```\r\npip install esptool\r\nesptool.py --chip esp32p4 -p PORT write_flash 0x0 meck-p4-0.3.5-version-merged.bin\r\n```\r\n\r\n(Replace `PORT` with `/dev/cu.usbmodemXXXX` on macOS, `/dev/ttyACM0` on Linux, or `COM3` on Windows.)\r\n\r\nUpgrading from v0.3.3 does not require `erase_flash`. The prefs loader continues to tolerate older shorter-blob layouts, so any new fields come up at default rather than wiping existing settings.\r\n\r\n> ⚠️ **AMOLED variant remains untested by the maintainer.** The `-amoled.bin` is built from the same source tree with the AMOLED display option selected in menuconfig, but the project maintainer doesn't have the AMOLED hardware on hand. The TFT variant is the tested and known-working build.\r\n\r\n## What's New Since v0.3.3\r\n\r\n### Direct Messaging\r\n\r\nOpen a companion contact's detail screen (from the Contacts menu tile on the home screen) and tap the cyan **Send DM** button (stacked above the red Delete button) to compose. The DM conversation view uses the standard chat layout — keyboard on the bottom half, message bubbles scrolling above.\r\n\r\n- Per-contact 20-message ring buffers in PSRAM, lazy-allocated so unused contacts cost nothing\r\n- Per-contact DM history persisted to SD and reloaded on boot\r\n- DM Inbox row in the channel picker shows a per-contact unread badge, refreshed every tick\r\n- Send-side ACK tracking — outgoing bubbles update from \"Sending...\" through to \"Delivered\" or \"Failed\" as the ACK round-trip completes\r\n\r\n### Room Server Support\r\n\r\nTap a Room-type contact to open the same admin login flow as repeaters. On successful login, the room's post timeline appears as a scrollable bubble list, left-aligned, with the author name above each bubble and a timestamp and hop count footer below.\r\n\r\n- Post history persisted per-room to SD and loaded on boot, so you don't need to re-login to see what's already been received\r\n- Live re-render: posts arriving while you're sitting on the room view land in the bubble list without leaving the screen\r\n- Author resolution walks your contacts looking for a matc\n…",
      "notesHtml": "<p>Direct messaging arrives in v0.3.5 — compose, send, receive, and persist DMs from any companion contact, with unread badges viewable in the channel list. Room server support and repeater admin land alongside, giving you logged-in access to room post history and the full status/CLI/settings interface for remote repeater management. A new map screen renders OSM slippy tiles from the SD card with a GPS dot, contact markers, and pan/zoom. Plus a per-contact path editor, a standalone trace route screen, a keyboard layout overhaul, a GPS time-sync fix, debug logs to SD, and config export to a MeshCore-app-compatible JSON file.</p>\n<blockquote>\n<p>⚠️ <strong>Still a pre-release.</strong> ⚠️ Web browser, IRC, and the BLE/WiFi companion path are still not in this build. See What's Not Yet Implemented for the full pending list.</p>\n</blockquote>\n<h2>First-Time Flashing: Read This First</h2>\n<p>Meck-P4 ships as a single <strong>merged binary</strong> (bootloader + partition table + application combined). One file, flash at offset <code>0x0</code>.</p>\n<blockquote>\n<p><strong>An SD card is recommended.</strong> With a FAT32-formatted card inserted, every saved setting, channel message, DM, and room post is mirrored automatically, audio files have somewhere to live, and the device recovers gracefully from a wiped NVS. Without an SD card the device still works but loses message history on reboot.</p>\n</blockquote>\n<p>Ensure you use the <strong>right-side USB-C port</strong> (the data port), not the high-speed charger port, to flash.\n<img alt=\"correct port usage for flashing\" src=\"https://github.com/user-attachments/assets/d3a7e17d-5efa-46c3-af5e-61f97f125569\" /></p>\n<h3>Flashing with the MeshCore Web Flasher (recommended)</h3>\n<ol>\n<li>Go to <a href=\"https://flasher.meshcore.io/\" target=\"_blank\" rel=\"noopener noreferrer\">https://flasher.meshcore.io/</a></li>\n<li>Scroll to the bottom and select <strong>Custom Firmware</strong></li>\n<li>Select the <code>meck-p4-0.3.5-version-merged.bin</code> file you downloaded</li>\n<li>Click OK on the merged-binary warning</li>\n<li>Click <strong>Flash</strong>, pick your device in the popup, and click <strong>Connect</strong></li>\n</ol>\n<h3>Flashing with esptool.py</h3>\n<pre><code>pip install esptool\nesptool.py --chip esp32p4 -p PORT write_flash 0x0 meck-p4-0.3.5-version-merged.bin\n</code></pre>\n<p>(Replace <code>PORT</code> with <code>/dev/cu.usbmodemXXXX</code> on macOS, <code>/dev/ttyACM0</code> on Linux, or <code>COM3</code> on Windows.)</p>\n<p>Upgrading from v0.3.3 does not require <code>erase_flash</code>. The prefs loader continues to tolerate older shorter-blob layouts, so any new fields come up at default rather than wiping existing settings.</p>\n<blockquote>\n<p>⚠️ <strong>AMOLED variant remains untested by the maintainer.</strong> The <code>-amoled.bin</code> is built from the same source tree with the AMOLED display option selected in menuconfig, but the project maintainer doesn't have the AMOLED hardware on hand. The TFT variant is the tested and known-working build.</p>\n</blockquote>\n<h2>What's New Since v0.3.3</h2>\n<h3>Direct Messaging</h3>\n<p>Open a companion contact's detail screen (from the Contacts menu tile on the home screen) and tap the cyan <strong>Send DM</strong> button (stacked above the red Delete button) to compose. The DM conversation view uses the standard chat layout — keyboard on the bottom half, message bubbles scrolling above.</p>\n<ul>\n<li>Per-contact 20-message ring buffers in PSRAM, lazy-allocated so unused contacts cost nothing</li>\n<li>Per-contact DM history persisted to SD and reloaded on boot</li>\n<li>DM Inbox row in the channel picker shows a per-contact unread badge, refreshed every tick</li>\n<li>Send-side ACK tracking — outgoing bubbles update from \"Sending...\" through to \"Delivered\" or \"Failed\" as the ACK round-trip completes</li>\n</ul>\n<h3>Room Server Support</h3>\n<p>Tap a Room-type contact to open the same admin login flow as repeaters. On successful login, the room's post timeline appears as a scrollable bubble list, left-aligned, with the author name above each bubble and a timestamp and hop count footer below.</p>\n<ul>\n<li>Post history persisted per-room to SD and loaded on boot, so you don't need to re-login to see what's already been received</li>\n<li>Live re-render: posts arriving while you're sitting on the room view land in the bubble list without leaving the screen</li>\n<li>Author resolution walks your contacts looking for a matc\n…</li>\n</ul>\n"
    },
    {
      "version": "v0.3.3",
      "name": "Meck-P4 v0.3.3: MeshCore Config Import, BQ27220 Calibration, UI Polish",
      "datetime": "2026-05-18T02:47:07Z",
      "url": "https://github.com/pelgraine/Meck-P4/releases/tag/v0.3.3",
      "prerelease": true,
      "notes": "One-step config import from the MeshCore mobile app so existing users can move to a P4 without re-entering everything, a firmware-side fix for the BQ27220 fuel gauge calibration so the battery percentage matches reality, and substantial UI work on the home screen, Settings, and tile layouts. Plus a small power-savings pass that trims about 38 mA off idle draw.\r\n\r\n> ⚠️ **Still a pre-release.** ⚠️ Direct messaging, roomserver access, repeater admin, trace route, web browser, IRC, and the BLE companion path are still not in this build. See What's Not Yet Implemented for the full pending list.\r\n\r\n## First-Time Flashing: Read This First\r\n\r\nMeck-P4 ships as a single **merged binary** (bootloader + partition table + application combined). One file, flash at offset `0x0`.\r\n\r\n> **An SD card is recommended.** With a FAT32-formatted card inserted, every saved setting and channel message is mirrored automatically, audio files have somewhere to live, and the device recovers gracefully from a wiped NVS. Without an SD card the device still works but loses message history on reboot.\r\n\r\nEnsure you use the **right-side USB-C port** (the data port), not the high-speed charger port, to flash.\r\n\r\n### Flashing with the MeshCore Web Flasher (recommended)\r\n\r\n1. Go to https://flasher.meshcore.io/\r\n2. Scroll to the bottom and select **Custom Firmware**\r\n3. Select the `meck-p4-0.3.3-version-merged.bin` file you downloaded\r\n4. Click OK on the merged-binary warning\r\n5. Click **Flash**, pick your device in the popup, and click **Connect**\r\n\r\n### Flashing with esptool.py\r\n\r\n```\r\npip install esptool\r\nesptool.py --chip esp32p4 -p PORT write_flash 0x0 meck-p4-0.3.3-version-merged.bin\r\n```\r\n\r\n(Replace `PORT` with `/dev/cu.usbmodemXXXX` on macOS, `/dev/ttyACM0` on Linux, or `COM3` on Windows.)\r\n\r\nUpgrading from v0.3 does not require `erase_flash`. The prefs loader has been made tolerant of older shorter-blob layouts, so any new fields added in v0.3.3 (such as Font Size) come up at default rather than wiping existing settings.\r\n\r\n> ⚠️ **AMOLED variant remains untested by the maintainer.** The `-amoled.bin` is built from the same source tree with the AMOLED display option selected in menuconfig, but the project maintainer doesn't have the AMOLED hardware on hand. The TFT variant is the tested and known-working build.\r\n\r\n## What's New Since v0.3\r\n\r\n### MeshCore App Config Import\r\n\r\nDrop your MeshCore mobile or desktop app config export onto the SD card, reboot, and your identity, channels, contacts, node name, and radio settings come across in one step. No more re-entering contacts or generating a fresh keypair when moving to the P4.\r\n\r\nHow to use:\r\n\r\n1. In the MeshCore mobile or desktop app, export your config as JSON\r\n2. Rename to `import.json` and copy to `/sdcard/meshcore/import.json` on the P4's SD card\r\n3. Reboot the device\r\n\r\nOn boot, Meck reads the file and applies it:\r\n\r\n| Field | Behaviour |\r\n|---|---|\r\n| Identity (private + public key) | Replaces `_main.id` atomically (tmp + rename). Existing identity is overwritten. |\r\n| Channels | Merged by 16-byte secret. Existing channels kept; new ones appended to free slots; duplicates skipped. |\r\n| Contacts | Merged by public key. Existing kept; new ones appended. Lat/lon converted from string-decimal to int32 e7. `custom_name` preferred over `name`. |\r\n| Node name | Replaces `prefs.node_name`. |\r\n| Radio settings | Replaces freq, BW, SF, CR, TX power on `P4NodePrefs`. Freq converted kHz to MHz, BW Hz to kHz. |\r\n\r\nOn success the file is moved to `/sdcard/meshcore/import.history/import-<unix_epoch>.json` so it doesn't re-apply on subsequent boots and leaves an audit trail. On parse failure (bad JSON, missing identity keys, wrong-length hex) a warning is logged and the file is left in place for inspection.\r\n\r\nImported names (node, channels, contacts) are stripped to printable ASCII before being stored. Meck-P4's Montserrat fonts cover Latin-1 only, so emoji codepoints would otherwise render as tofu boxes. Leading an\n…",
      "notesHtml": "<p>One-step config import from the MeshCore mobile app so existing users can move to a P4 without re-entering everything, a firmware-side fix for the BQ27220 fuel gauge calibration so the battery percentage matches reality, and substantial UI work on the home screen, Settings, and tile layouts. Plus a small power-savings pass that trims about 38 mA off idle draw.</p>\n<blockquote>\n<p>⚠️ <strong>Still a pre-release.</strong> ⚠️ Direct messaging, roomserver access, repeater admin, trace route, web browser, IRC, and the BLE companion path are still not in this build. See What's Not Yet Implemented for the full pending list.</p>\n</blockquote>\n<h2>First-Time Flashing: Read This First</h2>\n<p>Meck-P4 ships as a single <strong>merged binary</strong> (bootloader + partition table + application combined). One file, flash at offset <code>0x0</code>.</p>\n<blockquote>\n<p><strong>An SD card is recommended.</strong> With a FAT32-formatted card inserted, every saved setting and channel message is mirrored automatically, audio files have somewhere to live, and the device recovers gracefully from a wiped NVS. Without an SD card the device still works but loses message history on reboot.</p>\n</blockquote>\n<p>Ensure you use the <strong>right-side USB-C port</strong> (the data port), not the high-speed charger port, to flash.</p>\n<h3>Flashing with the MeshCore Web Flasher (recommended)</h3>\n<ol>\n<li>Go to <a href=\"https://flasher.meshcore.io/\" target=\"_blank\" rel=\"noopener noreferrer\">https://flasher.meshcore.io/</a></li>\n<li>Scroll to the bottom and select <strong>Custom Firmware</strong></li>\n<li>Select the <code>meck-p4-0.3.3-version-merged.bin</code> file you downloaded</li>\n<li>Click OK on the merged-binary warning</li>\n<li>Click <strong>Flash</strong>, pick your device in the popup, and click <strong>Connect</strong></li>\n</ol>\n<h3>Flashing with esptool.py</h3>\n<pre><code>pip install esptool\nesptool.py --chip esp32p4 -p PORT write_flash 0x0 meck-p4-0.3.3-version-merged.bin\n</code></pre>\n<p>(Replace <code>PORT</code> with <code>/dev/cu.usbmodemXXXX</code> on macOS, <code>/dev/ttyACM0</code> on Linux, or <code>COM3</code> on Windows.)</p>\n<p>Upgrading from v0.3 does not require <code>erase_flash</code>. The prefs loader has been made tolerant of older shorter-blob layouts, so any new fields added in v0.3.3 (such as Font Size) come up at default rather than wiping existing settings.</p>\n<blockquote>\n<p>⚠️ <strong>AMOLED variant remains untested by the maintainer.</strong> The <code>-amoled.bin</code> is built from the same source tree with the AMOLED display option selected in menuconfig, but the project maintainer doesn't have the AMOLED hardware on hand. The TFT variant is the tested and known-working build.</p>\n</blockquote>\n<h2>What's New Since v0.3</h2>\n<h3>MeshCore App Config Import</h3>\n<p>Drop your MeshCore mobile or desktop app config export onto the SD card, reboot, and your identity, channels, contacts, node name, and radio settings come across in one step. No more re-entering contacts or generating a fresh keypair when moving to the P4.</p>\n<p>How to use:</p>\n<ol>\n<li>In the MeshCore mobile or desktop app, export your config as JSON</li>\n<li>Rename to <code>import.json</code> and copy to <code>/sdcard/meshcore/import.json</code> on the P4's SD card</li>\n<li>Reboot the device</li>\n</ol>\n<p>On boot, Meck reads the file and applies it:</p>\n<table>\n<thead>\n<tr>\n<th>Field</th>\n<th>Behaviour</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>Identity (private + public key)</td>\n<td>Replaces <code>_main.id</code> atomically (tmp + rename). Existing identity is overwritten.</td>\n</tr>\n<tr>\n<td>Channels</td>\n<td>Merged by 16-byte secret. Existing channels kept; new ones appended to free slots; duplicates skipped.</td>\n</tr>\n<tr>\n<td>Contacts</td>\n<td>Merged by public key. Existing kept; new ones appended. Lat/lon converted from string-decimal to int32 e7. <code>custom_name</code> preferred over <code>name</code>.</td>\n</tr>\n<tr>\n<td>Node name</td>\n<td>Replaces <code>prefs.node_name</code>.</td>\n</tr>\n<tr>\n<td>Radio settings</td>\n<td>Replaces freq, BW, SF, CR, TX power on <code>P4NodePrefs</code>. Freq converted kHz to MHz, BW Hz to kHz.</td>\n</tr>\n</tbody></table>\n<p>On success the file is moved to <code>/sdcard/meshcore/import.history/import-&lt;unix_epoch&gt;.json</code> so it doesn't re-apply on subsequent boots and leaves an audit trail. On parse failure (bad JSON, missing identity keys, wrong-length hex) a warning is logged and the file is left in place for inspection.</p>\n<p>Imported names (node, channels, contacts) are stripped to printable ASCII before being stored. Meck-P4's Montserrat fonts cover Latin-1 only, so emoji codepoints would otherwise render as tofu boxes. Leading an\n…</p>\n"
    },
    {
      "version": "v0.3",
      "name": "Meck-P4 v0.3 -- Audio Player, Active Discovery, Internationalised Keyboard",
      "datetime": "2026-05-16T03:53:52Z",
      "url": "https://github.com/pelgraine/Meck-P4/releases/tag/v0.3",
      "prerelease": true,
      "notes": "The third Meck-P4 pre-release. Audio playback from SD, active zero-hop neighbour discovery with one-tap contact add, and a substantially upgraded virtual keyboard with light/dark theme, three physical layouts, and long-press accent input for French and Czech. Plus per-message hop and ACK metadata, and a fix for the battery cell capacity (which had been wrong since v0.1).\r\n\r\n> **⚠️ Still a pre-release.⚠️ ** Direct messaging, roomserver access, repeater admin, trace route, web browser, IRC, and the BLE companion path are still not in this build. See [What's Not Yet Implemented](#whats-not-yet-implemented) for the full pending list.\r\n\r\n---\r\n\r\n## First-Time Flashing -- Read This First\r\n\r\nMeck-P4 ships as a single **merged binary** (bootloader + partition table + application combined). One file, flash at offset `0x0`.\r\n\r\n> **An SD card is recommended.** With a FAT32-formatted card inserted, every saved setting and channel message is mirrored automatically, audio files have somewhere to live, and the device recovers gracefully from a wiped NVS. Without an SD card the device still works but loses message history on reboot.\r\n<img width=\"377\" height=\"284\" alt=\"correct port usage for flashing\" src=\"https://github.com/user-attachments/assets/c9b670d7-53ca-4da3-8506-03ad83590387\" />\r\n\r\nEnsure you use the right-side USB-C port (the data port), not the high-speed charger port, to flash.\r\n\r\n### Flashing with the MeshCore Web Flasher (recommended)\r\n\r\n1. Go to <https://flasher.meshcore.io/>\r\n2. Scroll to the bottom and select **Custom Firmware**\r\n3. Select the `meck-p4-0.3-version-merged.bin` file you downloaded\r\n4. Click OK on the merged-binary warning\r\n5. Click **Flash**, pick your device in the popup, and click **Connect**\r\n\r\n### Flashing with esptool.py\r\n\r\n```\r\npip install esptool\r\nesptool.py --chip esp32p4 -p PORT write_flash 0x0 meck-p4-0.3-version-merged.bin\r\n```\r\n\r\n(Replace `PORT` with `/dev/cu.usbmodemXXXX` on macOS, `/dev/ttyACM0` on Linux, or `COM3` on Windows.)\r\n\r\nIf you're upgrading from v0.1 and want to start with clean defaults, run `esptool.py --chip esp32p4 -p PORT erase_flash` before the write. Your contacts and prefs will be restored from the SD card mirror on first boot.\r\n\r\n> ⚠️ AMOLED variant is untested. The -amoled.bin is built from the same source tree with the AMOLED display option selected in menuconfig, but the project maintainer doesn't have the AMOLED hardware on hand and hasn't been able to flash or run it. Community members are currently testing it. If you have an AMOLED board, please report back via the [MeshCore Discord](https://discord.com/channels/1495203904898728149/1500323702859104457) or a GitHub issue with whether it boots, the display works, and touch works. The TFT variant is the tested and known-working build.\r\n\r\n---\r\n\r\n## What's New Since v0.1\r\n\r\n### Audio Player\r\n\r\nMP3 playback directly from the SD card. Two top-level subtrees give the player different defaults so music and audiobooks behave appropriately:\r\n\r\n| Subtree | Defaults |\r\n| --- | --- |\r\n| `/sdcard/audio/music/` | Standard playback. Resume bookmark off. |\r\n| `/sdcard/audio/audiobooks/` | Audiobook mode. Resume bookmark on, sleep timer available, position tracked through the playlist. |\r\n\r\n**How to use the audio player:**\r\n\r\n1. Drop your `music/` and `audiobooks/` folders into `/sdcard/audio/` on the SD card (the firmware creates the subtree automatically on first boot if it doesn't exist)\r\n2. Insert the card and power on\r\n3. Tap the **Audio** tile on the home grid to open the browser\r\n4. Navigate to a track and tap to play\r\n5. Use the transport row on the Now Playing screen for skip / play-pause / volume\r\n\r\n> **Read the [audio player guide](https://github.com/pelgraine/Meck-P4/blob/main/information/Meck%20Docs/audioplayerguide.md) before copying files to your SD card.** MP3s with large embedded album art can starve the IDLE task on first decode and trigger a watchdog reboot. The guide walks through the `tools/mp3_clean.py` script that strip\n…",
      "notesHtml": "<p>The third Meck-P4 pre-release. Audio playback from SD, active zero-hop neighbour discovery with one-tap contact add, and a substantially upgraded virtual keyboard with light/dark theme, three physical layouts, and long-press accent input for French and Czech. Plus per-message hop and ACK metadata, and a fix for the battery cell capacity (which had been wrong since v0.1).</p>\n<blockquote>\n<p>**⚠️ Still a pre-release.⚠️ ** Direct messaging, roomserver access, repeater admin, trace route, web browser, IRC, and the BLE companion path are still not in this build. See <a href=\"#whats-not-yet-implemented\" target=\"_blank\" rel=\"noopener noreferrer\">What's Not Yet Implemented</a> for the full pending list.</p>\n</blockquote>\n<hr />\n<h2>First-Time Flashing -- Read This First</h2>\n<p>Meck-P4 ships as a single <strong>merged binary</strong> (bootloader + partition table + application combined). One file, flash at offset <code>0x0</code>.</p>\n<blockquote>\n<p><strong>An SD card is recommended.</strong> With a FAT32-formatted card inserted, every saved setting and channel message is mirrored automatically, audio files have somewhere to live, and the device recovers gracefully from a wiped NVS. Without an SD card the device still works but loses message history on reboot.</p>\n<img alt=\"correct port usage for flashing\" src=\"https://github.com/user-attachments/assets/c9b670d7-53ca-4da3-8506-03ad83590387\" /></blockquote>\n<p>Ensure you use the right-side USB-C port (the data port), not the high-speed charger port, to flash.</p>\n<h3>Flashing with the MeshCore Web Flasher (recommended)</h3>\n<ol>\n<li>Go to <a href=\"https://flasher.meshcore.io/\" target=\"_blank\" rel=\"noopener noreferrer\">https://flasher.meshcore.io/</a></li>\n<li>Scroll to the bottom and select <strong>Custom Firmware</strong></li>\n<li>Select the <code>meck-p4-0.3-version-merged.bin</code> file you downloaded</li>\n<li>Click OK on the merged-binary warning</li>\n<li>Click <strong>Flash</strong>, pick your device in the popup, and click <strong>Connect</strong></li>\n</ol>\n<h3>Flashing with esptool.py</h3>\n<pre><code>pip install esptool\nesptool.py --chip esp32p4 -p PORT write_flash 0x0 meck-p4-0.3-version-merged.bin\n</code></pre>\n<p>(Replace <code>PORT</code> with <code>/dev/cu.usbmodemXXXX</code> on macOS, <code>/dev/ttyACM0</code> on Linux, or <code>COM3</code> on Windows.)</p>\n<p>If you're upgrading from v0.1 and want to start with clean defaults, run <code>esptool.py --chip esp32p4 -p PORT erase_flash</code> before the write. Your contacts and prefs will be restored from the SD card mirror on first boot.</p>\n<blockquote>\n<p>⚠️ AMOLED variant is untested. The -amoled.bin is built from the same source tree with the AMOLED display option selected in menuconfig, but the project maintainer doesn't have the AMOLED hardware on hand and hasn't been able to flash or run it. Community members are currently testing it. If you have an AMOLED board, please report back via the <a href=\"https://discord.com/channels/1495203904898728149/1500323702859104457\" target=\"_blank\" rel=\"noopener noreferrer\">MeshCore Discord</a> or a GitHub issue with whether it boots, the display works, and touch works. The TFT variant is the tested and known-working build.</p>\n</blockquote>\n<hr />\n<h2>What's New Since v0.1</h2>\n<h3>Audio Player</h3>\n<p>MP3 playback directly from the SD card. Two top-level subtrees give the player different defaults so music and audiobooks behave appropriately:</p>\n<table>\n<thead>\n<tr>\n<th>Subtree</th>\n<th>Defaults</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>/sdcard/audio/music/</code></td>\n<td>Standard playback. Resume bookmark off.</td>\n</tr>\n<tr>\n<td><code>/sdcard/audio/audiobooks/</code></td>\n<td>Audiobook mode. Resume bookmark on, sleep timer available, position tracked through the playlist.</td>\n</tr>\n</tbody></table>\n<p><strong>How to use the audio player:</strong></p>\n<ol>\n<li>Drop your <code>music/</code> and <code>audiobooks/</code> folders into <code>/sdcard/audio/</code> on the SD card (the firmware creates the subtree automatically on first boot if it doesn't exist)</li>\n<li>Insert the card and power on</li>\n<li>Tap the <strong>Audio</strong> tile on the home grid to open the browser</li>\n<li>Navigate to a track and tap to play</li>\n<li>Use the transport row on the Now Playing screen for skip / play-pause / volume</li>\n</ol>\n<blockquote>\n<p><strong>Read the <a href=\"https://github.com/pelgraine/Meck-P4/blob/main/information/Meck%20Docs/audioplayerguide.md\" target=\"_blank\" rel=\"noopener noreferrer\">audio player guide</a> before copying files to your SD card.</strong> MP3s with large embedded album art can starve the IDLE task on first decode and trigger a watchdog reboot. The guide walks through the <code>tools/mp3_clean.py</code> script that strip\n…</p>\n</blockquote>\n"
    },
    {
      "version": "v0.1",
      "name": "Meck-P4 v0.1 — First Pre-Release for the LilyGo T-Display P4",
      "datetime": "2026-05-09T08:14:03Z",
      "url": "https://github.com/pelgraine/Meck-P4/releases/tag/v0.1",
      "prerelease": true,
      "notes": "The first publicly flashable Meck firmware for the LilyGo T-Display P4. One\r\nfile, one offset, you're on the mesh.\r\n\r\n> **⚠️ This is a pre-release, not a finished product.** Many of the features people associate with Meck on the T-Deck Pro and T5S3 — direct messaging, roomserver access, repeater admin, trace route, web browser, IRC, audio player — are **not yet implemented** on the P4. What's here is the foundation: a fully functional MeshCore node with channel messaging, contacts, persistent storage, GPS, and battery monitoring. See the [full feature list and roadmap](https://github.com/pelgraine/Meck-P4#road-map--to-do).\r\n\r\n---\r\n\r\n## Hardware\r\n\r\n⚠️ LilyGo T-Display P4 (TFT version)⚠️  **The AMOLED variant has not been tested** but should be close — adjust the display init in\r\n`main/examples/lvgl_9_ui/main.cpp` if you want to try it.\r\n\r\n## Flashing\r\n<img width=\"377\" height=\"284\" alt=\"IMG_2838\" src=\"https://github.com/user-attachments/assets/0ed6126c-d51d-46b6-b71a-e51161ac9c40\" />\r\n\r\n\r\nEnsure you use the right-usb C port, ie **not** the high-speed charger port to flash.\r\n\r\nThe release file `meck-p4-0.1-merged.bin` is a **merged binary** containing the bootloader, partition table, and application combined into a single image — flash it at address `0x0`.\r\n\r\n**Using the MeshCore Flasher (web-based):**\r\n\r\n1. Go to <https://flasher.meshcore.io>\r\n2. Scroll to the bottom and select **Custom Firmware**\r\n3. Select the `meck-p4-0.1.bin` file you downloaded\r\n4. Click OK for the warning about erase files because it's a merged binary.\r\n5. Click **Flash**, choose your device in the popup, and click **Connect**\r\n\r\n**Using esptool.py:**\r\n\r\n```\r\npip install esptool\r\nesptool.py --chip esp32p4 -p PORT write_flash 0x0 meck-p4-0.1.bin\r\n```\r\n\r\n(Replace `PORT` with `/dev/cu.usbmodemXXXX` on macOS, `/dev/ttyACM0` on Linux, or `COM3` on Windows.)\r\n\r\nIf you've previously had something else on the device, run `esptool.py --chip esp32p4 -p PORT erase_flash` first to clear NVS so Meck starts with clean defaults.\r\n\r\n> **An SD card is recommended.** With a FAT32-formatted card inserted, every saved setting and channel message is mirrored automatically. The device works without one but loses message history on reboot.\r\n\r\n---\r\n\r\n## What's Working\r\n\r\n### 📡 Mesh networking\r\n\r\n- Channel messaging — send and receive on Public, #test, #sydney\r\n- 17-preset radio picker (AU, US, EU, CN regions). Boots on Australia Narrow by default (916.575 MHz / SF7 / BW 62.5 kHz / CR 4/8)\r\n- Adverts go out automatically; received adverts populate Recent Heard and (subject to your auto-add policy) the contact list\r\n- **Self-healing public-channel migration** — earlier dev builds derived the Public channel secret from a hash, which didn't match the rest of the network. v0.1 detects this on every boot and repairs it automatically, without disturbing your custom channels\r\n\r\n### 📱 UI\r\n\r\nA horizontal seven-tile home layout. Swipe left/right to switch tiles.\r\n\r\n| Tile | What it shows |\r\n| --- | --- |\r\n| 0 Home | Node name, freq/SF/RSSI/RX counters, six-button nav grid |\r\n| 1 Recent Heard | Live list of nodes whose adverts you've received |\r\n| 2 Radio Details | Current freq / BW / SF / CR / TX / sync word |\r\n| 3 Advert | Long-press to send a manual advert |\r\n| 4 GPS | Fix status, satellites, position, altitude, sentence rate |\r\n| 5 Battery | Voltage, charge%, current, chip temp, remaining mAh |\r\n| 6 Shutdown | Long-press to power down |\r\n\r\n### 👥 Contacts\r\n\r\n- Filter chip bar at the top: All / Chat / Rptr / Room / Sens / Fav. Swipe left/right to cycle, or tap a chip directly\r\n- Per-row colour-coded type pill (C / R / RS / S) and 4-byte pubkey prefix for disambiguation\r\n- **Long-press a contact** to toggle favourite (a star appears, the contact rises to the top)\r\n- Tap a contact to open the detail screen, with a red **Hold** button that long-press-deletes (single tap is unbound to prevent accidents)\r\n- Auto-add policies in Settings → Contacts: Auto All / Custom (per-type toggles) / Manu\n…",
      "notesHtml": "<p>The first publicly flashable Meck firmware for the LilyGo T-Display P4. One\nfile, one offset, you're on the mesh.</p>\n<blockquote>\n<p><strong>⚠️ This is a pre-release, not a finished product.</strong> Many of the features people associate with Meck on the T-Deck Pro and T5S3 — direct messaging, roomserver access, repeater admin, trace route, web browser, IRC, audio player — are <strong>not yet implemented</strong> on the P4. What's here is the foundation: a fully functional MeshCore node with channel messaging, contacts, persistent storage, GPS, and battery monitoring. See the <a href=\"https://github.com/pelgraine/Meck-P4#road-map--to-do\" target=\"_blank\" rel=\"noopener noreferrer\">full feature list and roadmap</a>.</p>\n</blockquote>\n<hr />\n<h2>Hardware</h2>\n<p>⚠️ LilyGo T-Display P4 (TFT version)⚠️  <strong>The AMOLED variant has not been tested</strong> but should be close — adjust the display init in\n<code>main/examples/lvgl_9_ui/main.cpp</code> if you want to try it.</p>\n<h2>Flashing</h2>\n<img alt=\"IMG_2838\" src=\"https://github.com/user-attachments/assets/0ed6126c-d51d-46b6-b71a-e51161ac9c40\" /><p>Ensure you use the right-usb C port, ie <strong>not</strong> the high-speed charger port to flash.</p>\n<p>The release file <code>meck-p4-0.1-merged.bin</code> is a <strong>merged binary</strong> containing the bootloader, partition table, and application combined into a single image — flash it at address <code>0x0</code>.</p>\n<p><strong>Using the MeshCore Flasher (web-based):</strong></p>\n<ol>\n<li>Go to <a href=\"https://flasher.meshcore.io/\" target=\"_blank\" rel=\"noopener noreferrer\">https://flasher.meshcore.io</a></li>\n<li>Scroll to the bottom and select <strong>Custom Firmware</strong></li>\n<li>Select the <code>meck-p4-0.1.bin</code> file you downloaded</li>\n<li>Click OK for the warning about erase files because it's a merged binary.</li>\n<li>Click <strong>Flash</strong>, choose your device in the popup, and click <strong>Connect</strong></li>\n</ol>\n<p><strong>Using esptool.py:</strong></p>\n<pre><code>pip install esptool\nesptool.py --chip esp32p4 -p PORT write_flash 0x0 meck-p4-0.1.bin\n</code></pre>\n<p>(Replace <code>PORT</code> with <code>/dev/cu.usbmodemXXXX</code> on macOS, <code>/dev/ttyACM0</code> on Linux, or <code>COM3</code> on Windows.)</p>\n<p>If you've previously had something else on the device, run <code>esptool.py --chip esp32p4 -p PORT erase_flash</code> first to clear NVS so Meck starts with clean defaults.</p>\n<blockquote>\n<p><strong>An SD card is recommended.</strong> With a FAT32-formatted card inserted, every saved setting and channel message is mirrored automatically. The device works without one but loses message history on reboot.</p>\n</blockquote>\n<hr />\n<h2>What's Working</h2>\n<h3>📡 Mesh networking</h3>\n<ul>\n<li>Channel messaging — send and receive on Public, #test, #sydney</li>\n<li>17-preset radio picker (AU, US, EU, CN regions). Boots on Australia Narrow by default (916.575 MHz / SF7 / BW 62.5 kHz / CR 4/8)</li>\n<li>Adverts go out automatically; received adverts populate Recent Heard and (subject to your auto-add policy) the contact list</li>\n<li><strong>Self-healing public-channel migration</strong> — earlier dev builds derived the Public channel secret from a hash, which didn't match the rest of the network. v0.1 detects this on every boot and repairs it automatically, without disturbing your custom channels</li>\n</ul>\n<h3>📱 UI</h3>\n<p>A horizontal seven-tile home layout. Swipe left/right to switch tiles.</p>\n<table>\n<thead>\n<tr>\n<th>Tile</th>\n<th>What it shows</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>0 Home</td>\n<td>Node name, freq/SF/RSSI/RX counters, six-button nav grid</td>\n</tr>\n<tr>\n<td>1 Recent Heard</td>\n<td>Live list of nodes whose adverts you've received</td>\n</tr>\n<tr>\n<td>2 Radio Details</td>\n<td>Current freq / BW / SF / CR / TX / sync word</td>\n</tr>\n<tr>\n<td>3 Advert</td>\n<td>Long-press to send a manual advert</td>\n</tr>\n<tr>\n<td>4 GPS</td>\n<td>Fix status, satellites, position, altitude, sentence rate</td>\n</tr>\n<tr>\n<td>5 Battery</td>\n<td>Voltage, charge%, current, chip temp, remaining mAh</td>\n</tr>\n<tr>\n<td>6 Shutdown</td>\n<td>Long-press to power down</td>\n</tr>\n</tbody></table>\n<h3>👥 Contacts</h3>\n<ul>\n<li>Filter chip bar at the top: All / Chat / Rptr / Room / Sens / Fav. Swipe left/right to cycle, or tap a chip directly</li>\n<li>Per-row colour-coded type pill (C / R / RS / S) and 4-byte pubkey prefix for disambiguation</li>\n<li><strong>Long-press a contact</strong> to toggle favourite (a star appears, the contact rises to the top)</li>\n<li>Tap a contact to open the detail screen, with a red <strong>Hold</strong> button that long-press-deletes (single tap is unbound to prevent accidents)</li>\n<li>Auto-add policies in Settings → Contacts: Auto All / Custom (per-type toggles) / Manu\n…</li>\n</ul>\n"
    }
  ],
  "changelogSource": "github",
  "changelogUpdatedAt": "2026-06-21T09:55:31.755Z"
}
