Off-grid Chicago: three solar-powered LoRa Meshtastic nodes, about $62 each, that prove multi-hop messaging across the city without the internet, the power grid or anyone's permission.
Version 1.0.0. Try it: order the parts below, then follow the build guide from Assemble to Verify the mesh. Firmware comes from the official Meshtastic web flasher.
- Send text messages across Chicago when the cell network, the internet or the power is down.
- Build a weatherproof, solar-powered node for about $62 in parts, roughly 30 minutes of assembly once the parts arrive.
- Run stock Meshtastic firmware, so your nodes join any compatible Meshtastic mesh with no fork to maintain.
- Survive a Chicago winter: the power chain uses LiFePO4 cells that charge safely below freezing, and the guide shows the December power budget.
- Provision three nodes identically from one PowerShell console instead of tapping through an app three times.
- Prove real multi-hop routing (A reaches C via B), not a one-hop demo.
- A complete, compatibility-checked bill of materials in config/parts.json: 10 core parts per node, 3 shared consumables and 7 optional tools, each with price, specs and purchase links.
- A step-by-step build guide: assemble, flash firmware, configure, deploy, verify, troubleshoot, with a checkpoint at the end of each stage.
- Safety rules called out where they matter: LiFePO4 only, the TP5000 charge jumper set to LFP, and never USB-C while the cell is connected.
- A Windows console (
ChiMesh.Console.bat) that provisions a USB-connected node, runs a six-check healthcheck, lists the parts catalog and records the deals you pick. - Placement guidance for Chicago: high-rise windows, balconies, rooftops and ground poles, solar aim and antenna orientation.
- Choices for region (US 915, EU 868, AU 915, AS 923), node role, deployment spot and antenna, with notes for each below.
Prices are per-node estimates in USD as of 2026-05-22. These tables are static and maintained by hand from config/parts.json, the canonical parts and price data; there is no interactive configurator. Check current prices before you order. Buy links go to the official store where the catalog has one, otherwise to a search.
One of each per node.
| Part | Qty | Unit price | Buy |
|---|---|---|---|
| RAK4631 WisBlock Core (nRF52840 + SX1262 LoRa) | 1 | $25 | store.rakwireless.com |
| RAK19003 WisBlock Mini Base Board | 1 | $10 | store.rakwireless.com |
| LiFePO4 18650 Cell (IFR18650, ~1500 mAh) | 1 | $5 | amazon.com |
| Single 18650 Battery Holder with Leads | 1 | $1 | amazon.com |
| TP5000 Solar Charge Controller (LFP-configurable) | 1 | $2 | amazon.com |
| 5V / 2W Monocrystalline Solar Panel (~110 x 80 mm) | 1 | $5 | amazon.com |
| 915 MHz SMA Antenna (~5 dBi rubber duck) | 1 | $5 | store.rakwireless.com |
| IPEX (u.FL) to SMA Pigtail, ~10 cm | 1 | $3 | amazon.com |
| IP65 ABS Weatherproof Junction Box (~100 x 68 x 50 mm) | 1 | $5 | amazon.com |
| M12 IP68 Cable Gland (for solar panel pass-through) | 1 | $1 | amazon.com |
| Total per node | $62 |
Multiply by your node count. You need at least 3 nodes to prove multi-hop routing, so the proof batch is about $186 in core parts.
One kit covers all nodes.
| Part | Qty | Unit price | Buy |
|---|---|---|---|
| JST-PHR-2 connectors + 22 AWG silicone wire + M3 screws | 1 | $8 | amazon.com |
| Heat-shrink tubing assortment (2-8 mm) | 1 | $8 | amazon.com |
| 60/40 rosin-core solder, 0.8 mm | 1 | $8 | amazon.com |
| Total consumables | $24 |
Excluded from the build estimate.
| Part | Qty | Unit price | Buy |
|---|---|---|---|
| Temperature-controlled soldering iron | 1 | $50 | pine64.com |
| Digital multimeter | 1 | $25 | amazon.com |
| Wire strippers + flush cutters | 1 | $15 | amazon.com |
| JST crimp tool (for PHR-2 terminals) | 1 | $20 | amazon.com |
| Heat gun (mini) or lighter | 1 | $15 | amazon.com |
| Drill + bits (6 mm + 12 mm step or twist) | 1 | $30 | amazon.com |
| Phillips #1 screwdriver | 1 | $5 | amazon.com |
| Total tools, if you own none | $160 |
| Solar panel | Charge controller | Enclosure |
|---|---|---|
![]() |
![]() |
![]() |
All ten part photos are in config/images.
- Order the core parts for 3 nodes, one consumables kit, and any tools you lack (see Parts list).
- Set each TP5000 jumper to LFP (3.6 V), then assemble each node with the cell holder left empty.
- Flash stock Meshtastic 2.5.x from flash.meshtastic.org in Chrome or Edge.
- Install the Meshtastic CLI and provision each node from this repo (commands below).
- Unplug USB-C, insert the LiFePO4 cell, and place the nodes so at least one pair cannot hear each other.
- Send a message from your phone (paired to node A) to node C. You should see it arrive with a "via B" hop indicator.
Step 4, with one node connected over USB:
pip install --upgrade meshtastic
git clone https://github.com/mindattic/ChiMesh.git
cd ChiMesh
ChiMesh.Console.bat provision chimesh-001
ChiMesh.Console.bat healthcheckThese two rules are non-negotiable for every outdoor node.
- LiFePO4, not Li-ion. Chicago winters charge below 0 °C. Li-ion cells permanently plate lithium when charged that cold and can fail dangerously by summer.
- Never plug USB-C into the RAK19003 while the LFP cell is wired into the JST. The RAK19003's onboard charger is Li-ion only (4.2 V termination) and will overcharge an LFP cell (3.65 V max). To re-flash: unplug the JST-PHR-2, plug USB-C, do your work, unplug USB-C, plug the JST back in.
Each node is one fixed signal chain. The repo holds the build guide, the parts catalog and the PowerShell tooling that talks to a node over USB through the meshtastic Python CLI.
PHYSICAL SIGNAL CHAIN (one node)
5V/2W solar -> TP5000 (LFP jumper set) -> LiFePO4 18650 -> JST-PHR-2 -> RAK19003 -> RAK4631
RAK4631 IPEX -> pigtail -> SMA bulkhead -> 915 MHz antenna
NODE TOOLING (scripts/cli, over USB)
ChiMesh.Console.ps1 --> provision-node.ps1 set region, role, channel 0, owner
--> healthcheck-mesh.ps1 verify region, role, channel, peers
PROOF OF MESH
phone --BLE--> Node A ~~LoRa~~> Node B ~~LoRa~~> Node C (A and C out of direct range)
| Layer | What it is |
|---|---|
| Radio | Semtech SX1262 LoRa, 915 MHz US ISM |
| MCU | Nordic nRF52840 (ARM Cortex-M4F @ 64 MHz) |
| Board | RAK4631 (RAK Wireless WisBlock Core) on RAK19003 Mini Base |
| Firmware | Meshtastic 2.5.x (stock, unmodified) |
| Mesh protocol | Meshtastic managed flood routing |
| Power | 5 V / 2 W solar, TP5000 (LFP-configured), LiFePO4 18650 |
| App and config | Meshtastic mobile or desktop app over BLE, or meshtastic CLI over USB |
| Enclosure | IP65 ABS junction box, M12 gland for the solar lead |
The catalog defines four choices in configAxes inside config/parts.json. The guide below notes where each one changes a step.
| Choice | Options | Default |
|---|---|---|
| LoRa region | US 915 MHz, EU 868 MHz, AU 915 MHz, AS 923 MHz | US 915 MHz |
| Node role | ROUTER_CLIENT (fixed, relays and chats), ROUTER (backbone, relay only), CLIENT (portable, low power) |
ROUTER_CLIENT |
| Deployment | Balcony or fence, apartment window, rooftop, ground pole or yard | Balcony or fence |
| Antenna | 5 dBi rubber duck, 3 dBi short whip, 8+ dBi fiberglass colinear (rooftop only) | 5 dBi rubber duck |
Total time is about 30 minutes per node once the parts are in hand.
One-time tools, not per node. Most builders already own them; the Parts list has picks if you do not.
- Temperature-controlled soldering iron and 60/40 rosin-core solder (0.8 mm)
- Heat-shrink tubing (2 to 8 mm assortment) and a heat gun or lighter
- Wire strippers and flush cutters for 22 AWG
- JST crimp tool, or buy pre-crimped JST-PHR-2 pigtails and skip it
- Digital multimeter, to check LFP cell voltage (3.2 to 3.65 V) and panel output (about 6.5 V open circuit)
- Drill and bits: 6 mm for the SMA bulkhead hole, 12 mm for the M12 cable gland (a step bit makes cleaner holes in ABS)
- Phillips #1 screwdriver for the enclosure lid
One signal path with two sub-paths:
Solar panel -> TP5000 (LFP jumper set!) -> LiFePO4 cell -> JST-PHR-2 -> RAK19003 -> RAK4631
RAK4631 IPEX -> pigtail -> SMA bulkhead -> 915 MHz antenna
Do not install the LFP cell until the Deploy stage. Flashing and configuring use USB-C on the RAK19003, whose onboard charger terminates at 4.2 V (Li-ion) and would overcharge an LFP cell (3.65 V max). The cell goes in after firmware and config are done.
- Set the TP5000 jumper to LFP (3.6 V). This is the single most important step. The board ships with the jumper in either position; the LFP setting is usually labelled
4.2/3.6orLi-ion/LiFePO4. Wrong jumper means a dead cell in weeks. - Solder the solar panel leads to the TP5000
IN+andIN-pads, red to plus, black to minus. Heat-shrink the joint. - Solder the LFP cell holder leads to the TP5000
BAT+andBAT-pads. Do not insert the cell yet. - Crimp a JST-PHR-2 onto a short pair of 22 AWG wires, then solder the other end to the TP5000
BAT+andBAT-pads, in parallel with the cell holder leads. This output feeds the RAK19003. - Snap the RAK4631 onto the RAK19003 Mini Base. Align the silk-screen markers and press until the board-to-board connector seats fully.
- Connect the IPEX pigtail. The u.FL connector on the RAK4631 is fragile: press straight down with a thumbnail until it clicks. Mount the SMA bulkhead through the drilled hole in the top of the enclosure, panel nut outside. Use SMA, not RP-SMA; they look identical but RP-SMA will not mate with a LoRa antenna.
- Plug the JST-PHR-2 from the TP5000 into the RAK19003 battery input. It only fits one way. Leave the cell holder empty: power for flashing and configuring comes from USB-C.
- Mount everything inside the enclosure. Foam-tape the TP5000 to one wall, the RAK19003 and Mini Base to the opposite wall, the empty cell holder along the floor. Route the panel cable out through the M12 gland.
- Visual check. The antenna is vertical when the enclosure is in its final orientation, no bare wire is near the SMA bulkhead, and the cell holder is empty.
Rooftop deployments: grounding is a real concern when the antenna is your highest point. Add a gas-discharge tube (GDT) lightning arrestor on the SMA line before the radio.
Checkpoint: boards mounted, antenna threaded through the enclosure wall, panel cable sealed at the gland, cell holder empty.
ChiMesh runs stock Meshtastic firmware. For v0 the value is in deployment and config conventions, not custom firmware.
- In Chrome or Edge (Web Serial is not in Firefox or Safari), open flash.meshtastic.org.
- Plug the RAK19003's USB-C into your computer.
- Click Select Device and pick RAK4631.
- Pick the latest stable Meshtastic 2.5.x build.
- Pick Region US (the default; adjust if you are outside the US).
- Click Flash and allow the browser's serial-port prompt.
- Wait about 30 seconds. The board reboots into Meshtastic automatically.
Checkpoint: the flasher prints "Done" and the RAK19003 LED settles into a slow heartbeat.
You can configure a node with the official Meshtastic app over Bluetooth or with the meshtastic CLI over USB. The CLI is faster for setting up three nodes identically.
Install the CLI. You should see meshtastic-python 2.5.x or newer.
pip install --upgrade meshtasticProvision the node with the bundled script, which walks one USB-connected node through the standard ChiMesh config and reads it back:
scripts\cli\provision-node.ps1 -NodeName chimesh-001Or set each field by hand:
meshtastic --set lora.region US
meshtastic --set device.role ROUTER_CLIENT
meshtastic --ch-set name "ChiMesh-Test" --ch-index 0
meshtastic --set-owner chimesh-001Notes for non-default choices:
ROUTERrelays every packet it hears but never originates user messages. Reserve it for true backbone nodes, usually one per metro area.ROUTER_CLIENTis better for most nodes because it relays and lets you chat.CLIENTis for portable or handheld use; the node sleeps aggressively and a sleeping client will not forward packets. Do not use it for fixed proof nodes.- EU 868: duty-cycle rules limit each device to about 1% airtime on most sub-channels. Meshtastic enforces this in firmware; expect slower throughput than US 915.
Confirm the config. You should see your region, your role, and channel 0 named ChiMesh-Test.
meshtastic --infoCheckpoint: meshtastic --info returns region, role and channel exactly as expected.
With firmware flashed and config written, unplug USB-C from the RAK19003. Insert the LFP cell into the holder. The RAK19003 red LED should blip briefly as the board powers up from the TP5000 BAT output. From now on, never plug in USB-C unless you have first unplugged the JST-PHR-2.
To re-flash later: unplug the JST, plug USB-C, flash, unplug USB-C, plug the JST back in. Tedious but safe.
For proof of mesh, place the 3 nodes so at least one pair cannot hear each other directly. Otherwise you have built a one-hop network and proven nothing about multi-hop routing.
A workable Chicago layout:
| Node | Suggested location | Why |
|---|---|---|
| Node A | High-rise apartment window or balcony | Gateway altitude, sees both B and C |
| Node B | Friend's apartment or office, about 1 to 3 km away | Far enough that it cannot reach C directly |
| Node C | Second remote spot, about 1 to 3 km from B in another direction | Forces traffic through A |
Notes by deployment:
- Apartment window: indoor through low-E glass costs about 10 to 15 dB of signal. Use a balcony, exterior fence or eave if you can; an antenna outside the window beats one inside every time.
- Rooftop: the strongest placement. Aim the solar panel south at about 41° tilt (Chicago's latitude) and keep the antenna clear of metal HVAC, parapets and railings.
- Ground pole: range is roughly half that of an elevated node because buildings, cars and foliage block line of sight. Useful as a yard pin, not as a gateway; do not expect to bridge beyond about 500 m.
- 8+ dBi fiberglass antenna: heavier, more visible and needs a real mast or bracket. Pair it with a rooftop deployment only.
Solar panel aim: face south, tilt about 41°. Do not lay panels flat; flat panels collect snow and droppings, and the low December sun cuts flat-mount harvest by about 30%.
Antenna orientation: vertical. Most Meshtastic networks are vertically polarized; a sideways antenna costs about 3 dB.
Rough draw for the RAK4631 (SX1262 and nRF52840) running stock Meshtastic in continuous receive:
- SX1262 in receive: about 4.6 mA
- nRF52840 active (BLE off, CPU lightly loaded): about 3 to 6 mA
- Combined at 3.3 V: about 8 to 12 mA average while listening, with brief spikes to about 120 mA on transmit
Over 24 hours that is about 200 to 280 mAh. The roughly 1500 mAh LFP cell and 5 V / 2 W panel work for a Chicago summer, where a sunny day harvests about 600 to 800 mAh. December is tight: 1.5 peak sun hours at 2 W gives about 150 to 200 mAh a day in, against about 250 mAh out. If a node has to survive January with no intervention, add a second cell in parallel (about 3000 mAh total) or step up to a 5 W panel.
Checkpoint: all 3 nodes deployed, panels facing the sky, antennas vertical, enclosure lids closed and sealed.
- Install the Meshtastic app from meshtastic.org/docs/software/apps and pair it with Node A over Bluetooth (default PIN
123456unless you changed it). - Open the Nodes tab. All 3 nodes should appear within a few minutes: A directly, B and C over radio. The hop count next to each remote node reads
1 hopfor direct and2 hopsfor routed. - Send a text from the app (paired to A) to Node C. If it arrives and the path shows
via B, you have proven multi-hop routing.
If every node shows 1 hop, the nodes are too close together. Separate them until at least one pair cannot see each other.
Then run the bundled healthcheck against whichever node is on the USB cable. It queries --info and --nodes and reports region, role, channel 0 name and the count of known peers.
scripts\cli\healthcheck-mesh.ps1Checkpoint: a message from your phone (paired to A) reaches C with a via B hop indicator.
- Use Chrome or Edge; Web Serial is not in Firefox or Safari.
- Try a different USB-C cable. Many charge-only cables do not carry data.
- On Windows, check Device Manager for the COM port; if it is missing, install Nordic's USB driver.
- The RAK19003 red LED only lights when current flows. If the cell is dead or the jumper is wrong, the board sits at 0 V.
- Check cell voltage with a multimeter: a healthy LFP cell reads 3.2 to 3.65 V.
- Confirm the TP5000 jumper is set for LFP (3.6 V), not Li-ion (4.2 V).
- Check that all nodes use the same region (
meshtastic --infoshowslora.region). - Check that all nodes use the same channel name and PSK.
- Move one node within 10 m of another to separate a config bug from a range problem.
- Check the panel: disconnect it from the TP5000 and measure open-circuit voltage in sunlight. Expect about 6.5 V, closer to 5 V under load.
- Check charging: reconnect and measure across the cell. It should rise toward 3.6 V over an hour of sun.
- December is the hard case. Upgrade to a 5 W panel or add a second LFP cell in parallel.
Run the healthcheck verbosely; it reads the connected node and prints any error states from --info.
scripts\cli\healthcheck-mesh.ps1 -VerboseLaunch the interactive menu from the repo root with ChiMesh.Console.bat, or dispatch one command with ChiMesh.Console.bat <command> [args].
| Command | What it does |
|---|---|
help |
List the commands (also shown with no arguments, as a looping menu). |
provision <node-name> |
Run provision-node.ps1: set region, role, channel 0 and owner on the USB-connected node and read back to confirm. Options: -Region, -Role, -Channel, -Port. |
healthcheck |
Run healthcheck-mesh.ps1, the six-check smoke test. Options: -Port, -Verbose. |
list-parts |
List the parts catalog by category, with price and any chosen purchase URL. |
find-deals |
Open search, official, Amazon and reputable-seller tabs per part (core, consumable or --all), then save the URL you paste back to the part's chosen field. |
pull-latest [--force] |
Fetch the latest tracked branch into a temp clone and hand off to pull-latest-finisher.ps1, which overlays it onto the repo after the console exits. --force overwrites a dirty working tree. |
provision-node.ps1 accepts -Region US, EU_868, AU_915 or AS_923 (default US), -Role ROUTER_CLIENT, CLIENT, ROUTER or CLIENT_MUTE (default ROUTER_CLIENT) and -Channel (default ChiMesh-Test).
Prerequisites:
- Windows PowerShell 5.1 (no
pwshneeded) pip install --upgrade meshtastic, forprovisionandhealthcheckgiton PATH, forpull-latest
Parts, prices and version facts are data. Edit these files rather than restating their values in prose.
| File | Purpose |
|---|---|
| config/parts.json | The parts catalog: categories (core, consumable, tools), the four build-option axes, per-part price, specs and purchase tiers (official, Amazon, reputable), and chosen URLs saved by find-deals. Registered as canon data through docs/data/parts.json and validated by tools/codex.ps1 doctor against the schema in docs/data. |
| config/versions.json | Pinned upstream versions: Meshtastic firmware 2.5.x, CLI label, board labels, default region and channel, flasher and app URLs. Keep it in sync with this README when upstream ships a new major. |
| config/images | Ten per-part photos. |
ChiMesh/
README.md project page and build guide (this file)
ChiMesh.Console.bat root shortcut to scripts/cli/ChiMesh.Console.bat
config/
parts.json parts catalog, build options, prices
versions.json pinned upstream versions
images/ 10 part photos
scripts/cli/
ChiMesh.Console.ps1 dispatch-table console
ChiMesh.Console.bat Windows launcher
provision-node.ps1 set region, role, channel 0, owner on a USB node
healthcheck-mesh.ps1 six-check smoke test
pull-latest-finisher.ps1 finishes pull-latest after the console exits
tools/
codex.ps1 doctor (validate docs) and digest
build-readme.ps1 regenerates README.htm from README.md
docs/
BIBLE.md architecture, laws, verified state
BIBLE.digest.md generated by codex.ps1 digest
AMENDMENTS.md pending decisions (normally empty)
USER_STORIES.md acceptance stories with test citations
rfc/ design notes
data/ parts registration and schema
There is no application build or unit-test suite. The in-repo verifier is the Codex doctor, which checks front matter, unique IDs, cross-references, parts rows against the schema, story test citations, cited paths and digest freshness:
powershell -NoProfile -ExecutionPolicy Bypass -File tools\codex.ps1 doctorRegenerate docs/BIBLE.digest.md after changing the bible:
powershell -NoProfile -ExecutionPolicy Bypass -File tools\codex.ps1 digestRegenerate README.htm after editing this file:
powershell -NoProfile -ExecutionPolicy Bypass -File tools\build-readme.ps1- The physical proof of mesh (A reaches C via B) has not yet been demonstrated in this repo.
provision-node.ps1andhealthcheck-mesh.ps1need real RAK4631 hardware and themeshtasticCLI, so they are not exercised in CI.- Channel 0 keeps the Meshtastic default PSK; a real PSK is deferred to deployment (see RFC 0001).
- December power is marginal: harvest barely clears draw on the worst weeks.
- Assemble and deploy three nodes and capture the
via Bhop. - Generate and distribute a secured channel PSK.
- More winter margin with a second cell or a 5 W panel.
- v2: a disguised, lamp-style enclosure, once the protocol is proven.
- docs/BIBLE.md: architecture, the seven ChiMesh laws, glossary and verified state
- User stories: acceptance stories and their test citations
- docs/AMENDMENTS.md: pending decisions not yet folded into the bible (normally empty)
- docs/rfc/0001-secured-channel-psk.md: secured channel design note
- AGENTS.md: instructions for AI agents working in this repo
This README on GitHub is the project page; there is no separate website.
External references:
- Meshtastic firmware
- Meshtastic web flasher
- Meshtastic apps
- RAK4631 product page
- RAK19003 product page
This repo has no LICENSE file. All rights reserved.
Part of MindAttic — see more projects at github.com/mindattic. Related: Claudia, another MindAttic hardware build.



