I wanted a throw-in-the-ruck box that could do three things without a laptop:
send/receive mesh messages, 2) drop GPS breadcrumbs, 3) do quick RF sanity checks.
Nomad 2 already has the radio, GPS, small screen, and keyboard, so I tried to make a boring, reliable setup I could bring up in under an hour.
- send/receive mesh messages, 2) drop GPS breadcrumbs, 3) do quick RF sanity checks.Nomad 2 already has the radio, GPS, small screen, and keyboard, so I tried to make a boring, reliable setup I could bring up in under an hour.
A lightweight workflow on Nomad 2 (Pi 4 or Pi 5) that:
- boots cleanly,
- logs mesh packets automatically,
- shows GPS status and stores a simple breadcrumb file,
- (optional, Recon model) runs a quick SDR check.
It’s not about max range or fancy dashboards—just dependable basics I can repeat.
Build notes (the short version)First boot
- Flash Raspberry Pi OS (64-bit), insert SD, attach LoRa whip + GPS puck.
- Join Wi-Fi/Ethernet,
sudo raspi-config
→ enable SSH.
Mesh messages with the CLI
sudo apt update
sudo apt install -y python3-pip
pip3 install --upgrade meshtastic
meshtastic --info # sanity check the onboard radio link
Auto-logging on boot
# /etc/systemd/system/meshlog.service
[Unit]
Description=Meshtastic packet logger
After=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/meshtastic --subscribe >> /home/pi/mesh.log 2>&1
Restart=always
User=pi
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now meshlog
tail -f ~/mesh.log
GPS fix + breadcrumbs
sudo apt install -y gpsd gpsd-clients
sudo systemctl stop gpsd.socket gpsd
sudo gpsd -N -n /dev/ttyAMA0 -F /var/run/gpsd.sock # adjust if needed
cgps -s
Tiny CSV logger I used:
python3 - << 'PY'
import time, json, subprocess, csv, os
p = os.path.expanduser('~/breadcrumbs.csv')
hdr = ['ts','lat','lon','alt','speed','track']
exists = os.path.exists(p)
with open(p,'a',newline='') as f:
w = csv.writer(f);
if not exists: w.writerow(hdr)
while True:
try:
out = subprocess.check_output(['gpspipe','-w','-n','10']).decode()
j = json.loads(out)
if j.get('class')=='TPV' and 'lat' in j and 'lon' in j:
w.writerow([time.time(), j['lat'], j['lon'],
j.get('alt',''), j.get('speed',''), j.get('track','')])
f.flush()
except Exception: pass
time.sleep(5)
PY
(Optional) SDR quick check on Recon
sudo apt update
sudo apt install -y rtl-sdr gqrx-sdr
rtl_test
gqrx
Small quality-of-life
tmux
for sticky session- Crontab heartbeat every 2 minutes:
printf '#!/usr/bin/env bash\nmeshtastic --sendtext "HB $(date +%H:%M)"\n' > ~/hb.sh
chmod +x ~/hb.sh
(crontab -l; echo "*/2 * * * * $HOME/hb.sh") | crontab -
What happened in the field- Rooftop vs backpack: On a low rooftop, messages landed more consistently than when the unit was buried in the pack. Antenna line-of-sight mattered more than I expected at short distances.
- Retries are fine: Short texts occasionally needed a retry in cluttered streets; the CLI made it trivial.
- GPS patience: Cold start took a bit; reacquisition was quick after that.
- Power reality: Screen brightness moves the needle. I kept it low and carried a small power bank for a top-off.
- Pi 4 vs Pi 5: For messaging + GPS + occasional scans, Pi 4 stayed cooler and felt “quiet.” If I were decoding heavier stuff, I’d pick Pi 5 and add more cooling.
- Serial names: Double-check which
/dev/tty*
the radio/GPS use; a quickdmesg | grep -i tty
saved time. - SDR gain: Looks pretty until it clips—start conservative.
- Wi-Fi power save: If SSH feels flaky, check power management on the Wi-Fi adapter.
- Cable placement: Keeping the LoRa whip clear of metal helped consistency.
- A single “kiosk” screen with last 10 messages, GPS status, and battery.
- Map tile cache + simple breadcrumb viewer for no-data areas.
- A “gateway” mode that buffers off-grid traffic and syncs over LTE only when available.
Fresh OS, SSH on
- Fresh OS, SSH on
pip3 install meshtastic
→--info
worksgpsd
up,cgps -s
shows a fix- (Recon)
rtl_test
OK, save a sane GQRX profile meshlog.service
enabled and writing to~/mesh.log
That’s it—simple, repeatable, and easy to pack
Comments