Skip to content

Commit ebe93a6

Browse files
committed
copy edit
1 parent 9f4175e commit ebe93a6

File tree

2 files changed

+29
-30
lines changed

2 files changed

+29
-30
lines changed

2025-09-potato-mesh.html

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<html>
33
<head>
44
<meta charset="UTF-8">
5-
<title>2025-09 Potato-Mesh what and why</title>
5+
<title>2025-09 PotatoMesh what and why</title>
66
<style>
77
body {
88
background: #fdf5e6;
@@ -67,30 +67,31 @@
6767
</head>
6868
<body>
6969
<header class="page-top">
70-
<img src="./animated-under-construction-image-0053.gif" alt="Under Construction Sign" />
71-
<h1>2025-09 Potato-Mesh what and why</h1>
70+
<h1>2025-09 PotatoMesh what and why</h1>
7271
<p><a class="back-home" href="./index.html">&larr; Return to the homepage</a></p>
7372
</header>
7473

7574
<article class="post">
76-
<p>Below is a technical sketch of <strong>Potato-Mesh</strong>: how it works, how it plugs into Meshtastic, what data flows where, and how you can run your own. Doom optional, but the mesh will outlive us if we get this right.</p>
75+
<p>
76+
I have been hacking on <strong>PotatoMesh</strong> for the last couple of weeks and here are some insights. I'll give you a technical sketch: how it works, how it plugs into Meshtastic, what data flows where, and how you can run your own. (Spoiler: it's white label!)
77+
</p>
7778

7879
<hr />
7980

80-
<h2>Why a tool like Potato-Mesh is needed</h2>
81+
<h2>Why a tool like PotatoMesh is needed</h2>
8182
<p>
82-
Meshtastic gives you resilient, off-grid mesh networks using LoRa. But by itself, its hard to get a global view of whats going on in a community mesh: who is alive, how many hops, where are telemetry gaps, which radios are misbehaving. During quiet times, we need visibility so that when crisis or scale hits, debugging, preparation, and maintenance are possible. Potato-Mesh is one of the tools built for that.
83+
Meshtastic gives you resilient, off-grid mesh networks using LoRa. But by itself, it's hard to get a global view of what's going on in a community mesh: who is alive, how many hops, where are telemetry gaps, which radios are misbehaving. During "quiet" times, we need visibility so that when crisis or scale hits, debugging, preparation, and maintenance are possible. PotatoMesh is one of the tools I built for that.
8384
</p>
8485

8586
<hr />
8687

87-
<h2>Architecture of Potato-Mesh: web app + python ingestor</h2>
88+
<h2>Architecture of PotatoMesh: Ruby web app + Python ingestor</h2>
8889
<p>
89-
At its core, the Potato-Mesh web app is simple: a SQLite database live on a server. It <strong>does not</strong> poll the mesh, nor listen on LoRa directly. It knows nothing until someone <strong>reports</strong> mesh data (nodes, messages) via its API endpoints. The API is the only inlet for the mesh state.
90+
At its core, the PotatoMesh web app is simple: a SQLite database live on a server. It <strong>does not</strong> poll the mesh, nor listen on LoRa directly. It knows nothing until someone <strong>reports</strong> mesh data (nodes, messages, positions, etc.) via its API endpoints. The API is the only inlet for the mesh state of the web app.
9091
</p>
9192
<ul>
9293
<li><strong>Database</strong>: SQLite, storing node records, message history, telemetry (as reported).</li>
93-
<li><strong>Web UI</strong>: map view, chat/message view, table of nodes; search / filter; first-seen node alerts. Authenticated POSTs feed it; GETs allow inspection.</li>
94+
<li><strong>Web UI</strong>: map view, chat/message view, table of nodes; search / filter; first-seen new node alerts. Authenticated POSTs feed it; GETs allow inspection.</li>
9495
</ul>
9596

9697
<h3>API endpoints</h3>
@@ -101,53 +102,53 @@ <h3>API endpoints</h3>
101102
</thead>
102103
<tbody>
103104
<tr><td><code>GET /api/nodes?limit=100</code></td><td>fetch recent nodes reported to the app (with their known metadata)</td><td></td></tr>
104-
<tr><td><code>GET /api/messages?limit=100</code></td><td>fetch recent messages seen in the mesh (chats, telemetry etc.)</td><td></td></tr>
105-
<tr><td><code>POST /api/nodes</code></td><td>Upsert nodes: send node data (JSON) mapping node IDs → node data; requires <code>Authorization: Bearer &lt;API_TOKEN&gt;</code></td><td></td></tr>
106-
<tr><td><code>POST /api/messages</code></td><td>Append new messages (JSON array or object); requires same auth token</td><td></td></tr>
105+
<tr><td><code>GET /api/messages?limit=100</code></td><td>fetch recent messages seen in the mesh (chats, reactions, etc.)</td><td></td></tr>
106+
<tr><td><code>POST /api/nodes</code></td><td>"Upsert" nodes: send node data (JSON); requires <code>Authorization: Bearer &lt;API_TOKEN&gt;</code></td><td></td></tr>
107+
<tr><td><code>POST /api/messages</code></td><td>Append new messages (JSON); requires same auth token</td><td></td></tr>
107108
</tbody>
108109
</table>
109110
<p>
110-
The <code>API_TOKEN</code> environment variable must be set (non-empty) on the web app to allow POSTs. GETs are open (or less restricted). (<a href="https://github.com/l5yth/potato-mesh">GitHub</a>)
111+
The <code>API_TOKEN</code> environment variable must be set (non-empty) on the web app to allow POSTs. GETs are open (or less restricted).
111112
</p>
112113

113114
<hr />
114115

115116
<h2>Inner workings: Meshtastic protocol, data ingestion</h2>
116117
<p>
117-
To fill those API endpoints, Potato-Mesh uses a <strong>python ingestor</strong> script (mesh.sh + supporting code under <code>./data/</code>) which connects to a <strong>local Meshtastic node</strong> over a serial port. Heres outline of how data flows and is processed.
118+
To fill those API endpoints, PotatoMesh uses a <strong>Python ingestor</strong> script ("mesh.sh" + supporting code under <code>./data/</code>) which connects to a <strong>local Meshtastic node</strong> over a serial or TCP port. Here's outline of how data flows and is processed.
118119
</p>
119120

120121
<h3>Meshtastic protocol basics</h3>
121122
<ul>
122-
<li>Meshtastic devices communicate via <strong>Protobufs</strong>: structured message definitions in <code>.proto</code> files. These define things like <code>NodeInfo</code>, <code>MyNodeInfo</code>, <code>User</code>, messages, telemetry, etc. (<a href="https://meshtastic.org/docs/development/reference/protobufs/?utm_source=chatgpt.com">meshtastic.org</a>)</li>
123+
<li>Meshtastic devices communicate via encrypted <strong>protobufs</strong>: structured message definitions. These define things like <code>NodeInfo</code>, <code>MyNodeInfo</code>, <code>User</code>, messages, telemetry, positions, etc.</li>
123124
<li>To talk to a Meshtastic node over USB/serial/TCP/BLE, there is a <strong>Client API</strong>. In that API:
124125
<ol>
125-
<li>When a client connects, it typically sends a <strong>startConfig</strong> (or <code>want_config_id</code>) protobuf to ask for the current NodeDB (nodes in mesh seen), the radio settings, user info, etc. (<a href="https://meshtastic.org/docs/development/device/client-api/?utm_source=chatgpt.com">meshtastic.org</a>)</li>
126-
<li>The node replies with a stream of <code>FromRadio</code> protobuf messages: first the configuration (radio config, user, mynode), then all known <code>NodeInfo</code> records, ending with an end-config marker. After that, mesh messages, telemetry, etc. (<a href="https://meshtastic.org/docs/development/device/client-api/?utm_source=chatgpt.com">meshtastic.org</a>)</li>
126+
<li>When a client connects, it typically sends a <strong>startConfig</strong> (or <code>want_config_id</code>) protobuf to ask for the current NodeDB (nodes in mesh seen), the radio settings, user info, etc.</li>
127+
<li>The node replies with a stream of <code>FromRadio</code> protobuf messages: first the configuration (radio config, user, mynode), then the last 80 known <code>NodeInfo</code> records, ending with an end-config marker. After that, mesh messages, positions, telemetry, etc.</li>
127128
</ol>
128129
</li>
129-
<li>Over serial or TCP, there is framing: i.e. Meshtastic uses a 4-byte header (START1=0x94, START2=0xc3, then two bytes length) to wrap each protobuf packet in streaming transports. If the stream is unreliable / loses bytes, there is synchronization logic. (<a href="https://meshtastic.org/docs/development/device/client-api/?utm_source=chatgpt.com">meshtastic.org</a>)</li>
130-
<li>Telemetry: nodes periodically report telemetry data (battery, environment, etc.) via defined protobuf messages. Also messages (text, chat), and node status / location / GPS. (<a href="https://pole1.co.uk/blog/6/?utm_source=chatgpt.com">pole1.co.uk</a>)</li>
130+
<li>Over serial or TCP, there is framing: i.e., Meshtastic uses a 4-byte header (START1=0x94, START2=0xc3, then two bytes length) to wrap each protobuf packet in streaming transports. If the stream is unreliable / loses bytes, there is synchronization logic.</li>
131+
<li>Telemetry: nodes periodically report telemetry data (battery, environment, etc.) via defined protobuf messages. Also messages (text, chat), and node status / location / GPS.</li>
131132
</ul>
132133

133-
<h3>Python ingestor in Potato-Mesh</h3>
134+
<h3>Python ingestor in PotatoMesh</h3>
134135
<p>What this script does:</p>
135136
<ol>
136-
<li><strong>Connect</strong> to a Meshtastic node on a serial port (e.g. <code>/dev/ttyACM0</code>), using the Meshtastic Python library. It sets things so that it receives the node database (NodeInfo etc.) and ongoing messages / telemetry as the mesh node sees them. (<a href="https://github.com/l5yth/potato-mesh">GitHub</a>)</li>
137+
<li><strong>Connect</strong> to a Meshtastic node on a serial port (e.g., <code>/dev/ttyACM0</code>), using the Meshtastic Python library. It sets things so that it receives the node database (NodeInfo etc.) and ongoing messages / telemetry as the mesh node sees them.</li>
137138
<li><strong>Parse</strong> each piece of data:
138139
<ul>
139-
<li>Whenever a <code>NodeInfo</code> or similar node status protobuf arrives, it constructs a node record (node ID, short name, etc.).</li>
140+
<li>Whenever a <code>NodeInfo</code> or similar node status protobuf arrives, it constructs a "node" record (node ID, short name, etc.).</li>
140141
<li>Whenever a message or telemetry arrives, collect the message: who from, to (broadcast or specific), type (text / telemetry / etc.), payload.</li>
141142
</ul>
142143
</li>
143-
<li><strong>POST</strong> to the web apps endpoints:
144+
<li><strong>POST</strong> to the web app's endpoints:
144145
<ul>
145146
<li>Node updates via <code>POST /api/nodes</code></li>
146147
<li>Message / telemetry / chat data via <code>POST /api/messages</code></li>
147148
</ul>
148-
<p>The tool (via environment variables) knows the instance URL and API token to use. It ensures deduplication on node IDs etc., so repeated reports of the same node do not create duplicates. (<a href="https://github.com/l5yth/potato-mesh">GitHub</a>)</p>
149+
<p>The tool (via environment variables) knows the instance URL and API token to use. The web app ensures deduplication on node IDs etc., so repeated reports of the same node do not create duplicates.</p>
149150
</li>
150-
<li><strong>Continuous listening / looping</strong>: the ingestor sits waiting for new protobuf messages via the serial stream. When new messages show up, theyre parsed &amp; forwarded. In debug mode you can see logs like upserted node … or stored message … etc. (<a href="https://github.com/l5yth/potato-mesh">GitHub</a>)</li>
151+
<li><strong>Continuous listening / looping</strong>: the ingestor sits waiting for new protobuf messages via the serial stream. When new messages show up, they're parsed &amp; forwarded. In debug mode you can see logs like "upserted node …" or "stored message …" etc. Just set "DEBUG=1".</li>
151152
</ol>
152153

153154
<hr />
@@ -161,17 +162,16 @@ <h2>Summary &amp; running</h2>
161162
</ul>
162163
<p>To try it:</p>
163164
<ul>
164-
<li>Demo is live at <strong>potatomesh.net</strong> for Berlin #MediumFast etc. (<a href="https://github.com/l5yth/potato-mesh">GitHub</a>)</li>
165-
<li>Instructions in the README: using Docker / docker-compose to launch the web app; configure environment (<code>API_TOKEN</code>, etc.). Then run the python ingestor with <code>POTATOMESH_INSTANCE=&lt;url&gt; API_TOKEN=&lt;token&gt; MESH_SERIAL=&lt;serial port&gt; DEBUG=1 ./mesh.sh</code> etc. (<a href="https://github.com/l5yth/potato-mesh">GitHub</a>)</li>
165+
<li>Demo is live at <strong>potatomesh.net</strong> for Berlin #MediumFast on 868MHz.</li>
166+
<li>Instructions in the README: using Docker / docker-compose to launch the web app; configure environment (<code>API_TOKEN</code>, etc.). Then run the python ingestor with <code>POTATOMESH_INSTANCE=&lt;url&gt; API_TOKEN=&lt;token&gt; MESH_SERIAL=&lt;serial port&gt; DEBUG=1 ./mesh.sh</code> etc.</li>
166167
</ul>
167168

168169
<hr />
169170

170171
<h2>Call to action</h2>
171172
<p>
172-
Run your own Potato-Mesh. Hook a Meshtastic node (or several), run the ingestor, set up the web app, and suddenly you dont fly blind. Debug routing, telemetry, battery, nodes that drop out, hops that get too high. The mesh always wants to talk — let’s listen.
173+
Run your own PotatoMesh. Hook a Meshtastic node (or several), run the ingestor, set up the web app, and suddenly you don't fly blind. Debug routing, telemetry, battery, nodes that drop out, hops that get too high.
173174
</p>
174-
<p>Doom threat: the mesh networks left unknowable will fail when they are needed most. Better to build clarity now.</p>
175175
</article>
176176

177177
<footer class="page-foot">

index.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ <h1>🚧 Under Construction 🚧</h1>
3434
<img src="./animated-under-construction-image-0053.gif" border="0" alt="Under Construction Sign" />
3535
<hr>
3636
<marquee scrollamount="4" behavior="alternate">Welcome to my homepage!</marquee>
37-
<p><a href="./2025-09-potato-mesh.html" target="_blank">📓 2025-09 Potato-Mesh what and why</a></p>
3837
<p><a href="mailto:COM0@l5y.tech" target="_blank">📧 Click here to email me!</a>
3938
(<a href="./com0.l5y.tech.pub.asc" target="_blank">PGP</a>)</p>
4039
<hr>

0 commit comments

Comments
 (0)