A mobile, in-vehicle MeshCore repeater that reports where it parks. When the vehicle stops for a few minutes it pushes its GPS location to both Meshtastic and MeshCore at once — so the node shows up where you actually left it, on both maps.
A fork of the meshtastic_beacon project. It reuses
that project's on-air-verified Meshtastic interop math unchanged, and replaces
the static, fixed-interval beacon with a park-triggered one that
updates both networks together.
| mtbeacon | car node | |
|---|---|---|
| Position source | configured lat/lon (static) | live GPS fix at park time |
| Trigger | fixed interval (minutes) | vehicle parks (stopped > park sec) + light presence every interval min |
| Networks updated | Meshtastic only | Meshtastic + MeshCore together |
| While driving | beacons periodically | presence only (NodeInfo, no position) at interval |
| CLI verb | mtbeacon … | carnode … |
| Config file | /mtbeacon | /carnode |
Location updates fire once the GPS fix sits still for park seconds (default 5 min); a light presence keeps the node visible in between.
The same fix updates the Meshtastic beacon and floods a fresh MeshCore advert in the same event.
Appears as a named node + map pin and optional text. Does not route or rebroadcast traffic.
Hold the user button ~3 s to drop the node into its lowest-power state; tap to wake.
carnode zone add <name> stores a place you park often — up to 4. Parking in one turns repeat off and suppresses every broadcast, so that spot never goes on the air.
nRF52 boards arm a hardware watchdog: a hung node reboots itself instead of riding around dead. get pwrmgt.bootreason tells you why the last reset happened.
If the radio's TxDone interrupt goes missing across a retune, the node reads the chip's own status flag to tell a delivered park update from one that never left the antenna — and retries the ones that didn't. After a run of dead transmits it re-initialises the radio rather than staying silent; mtbeacon stats shows the running totals.
Each burst carries Meshtastic telemetry, so a car parked for days shows its charge draining in any Meshtastic client.
The node watches its GPS fix. While the position keeps moving
outside radius metres it's driving and sends no location.
Once the fix sits within radius for park seconds
(default 5 min), the vehicle is parked and one unified update fires:
NodePrefs location and floods a fresh advert, so the
whole MeshCore mesh learns where you stopped.radius);
mtbeacon send forces an update immediately. The MeshCore
location is persisted, so it survives a reboot while parked.
radius
for ~15 s before it counts as driving. carnode status
shows the live tracking (d<n>m = filtered fix's
distance from the park anchor — (raw) while the filter warms
up, <n>min = how long the stationary clock has run);
if it still flips to driving while the car sits still, raise
carnode radius.
interval minutes (default 30, 0 = park-only)
so the node doesn't age out of Meshtastic node lists. While parked it
includes Position — refreshing the pin at the parked spot; while
driving (or without a fix) it sends NodeInfo only, so the map pin
never wanders off to a random mid-drive point. Location still updates
only at park time.
mtbeacon text message is
event-driven — whenever the repeater floods a MeshCore advert (the
park re-advert, the periodic flood advert, or an explicit CLI
advert), the text rides a Meshtastic burst a few seconds
later. Each new-spot park announces itself in chat, and running
advert doubles as a live test. mtbeacon status
shows the pacing live, e.g. txt1x~47h(due 13h) — or
(due now) when a text is armed. At home nothing transmits;
an armed text just waits until you drive away.
sleep
hours (default 20) the node stops forwarding — remote status reports
it disabled and it drops out of node-discover — because a car parked for a
day is probably somewhere nobody needs a mobile repeater. It still receives,
answers its own commands, adverts, and beacons, and forwarding resumes the
moment driving is detected. A GPS dropout while parked (underground garage)
does not wake it; only movement does. carnode sleep 0
disables it.
set repeat on|off pref
rather than written into it, so get repeat always reports what
you chose and no sleeping state can reach flash — a reboot always starts
awake. carnode status shows rpt:off while
suppression is active, the one place the two can disagree.
set repeat on once while parked outside any
zone; it clears for good.
carnode zone add home — the node stores the current spot
under that name (persisted). From then on, parking within the zone's
radius (default 100 m) turns repeat off immediately, no waiting for
the sleep timer, and keeps the node radio-quiet: the park burst, the
MeshCore re-advert and the periodic presence are all suppressed, so that
location is never put on the air (carnode send still
transmits if you explicitly ask). Driving away restores everything.
Up to 4 zones — home, work, a regular site — each with its own
radius; carnode zone lists them and marks the one you're in.
carnode home still works as shorthand for the zone named
home, and a home set on an older build migrates across
automatically.
hops 0 — heard by
direct neighbours, never rebroadcast. A car node transmits from wherever it
happens to park, so pushing its position out across someone else's mesh is
poor manners. mtbeacon hops <0-3> raises it if you
specifically need the reach.
carnode status shows adv!N if any park re-advert
was abandoned outright; normally it's absent.
Pre-built binaries for the three supported boards. The Heltecs need an external NMEA GPS module on their GPS UART pins; the T1000-E has GPS onboard. GPS defaults on.
invalid header: 0xffffff):
flash the -full image at 0x0 — it bundles the bootloader + partition table..bin at 0x10000.esptool.py write_flash 0x0 heltec_v4_carnode-4769627a-full.bin # fresh board
esptool.py write_flash 0x10000 heltec_v4_carnode-4769627a.bin # update
.zip via adafruit-nrfutil over serial, or flash the .hex with a debugger.adafruit-nrfutil dfu serial -pkg heltec_t114_carnode-dfu.zip -p /dev/ttyACM0 -b 115200
.uf2
onto the T1000-E USB drive that appears (keeps the stock bootloader).double-tap RST → copy t1000e_dualmode_carnode-4769627a.uf2 to the T1000-E drive
Checksums: SHA256SUMS.txt ·
Built from branch carnode (commit 4769627a). Not an official MeshCore release.
Once flashed, the ver command reports v1.16.0+carnode-4769627a — check it to confirm you're on this build.
Over USB serial or an admin remote-CLI session. The command
surface is split in two — one beacon engine, no duplicate transmitters:
mtbeacon tunes the Meshtastic beacon, carnode tunes
the car-specific park behaviour.
mtbeacon — the Meshtastic beacon (RF + appearance)| Command | Effect |
|---|---|
mtbeacon / status | show beacon RF config |
stats / stats clear | transmit health since boot / reset the counters |
on / off | enable / disable beaconing |
send | push a location update now (both networks) |
interval <min> | periodic presence between parks (0–1440, 0 = park-only, default 30) |
preset <name> | modem preset (LongFast, MediumFast, …) |
region <name> | region/country band (US, EU_868, …) |
freq <MHz|auto> | manual frequency override; auto re-derives |
power <dBm> | TX power (−9…22), capped to region limit |
hops <0-3> | Meshtastic hop limit for presence + text (default 0 = direct neighbours only) |
text <string> | announced chat text (≤63 chars) |
text.mult <N> | text pacing: rides each flood advert, plus N−1 timer-paced extras per period (0 = never) |
short <str|auto> | Meshtastic short name / map marker label (≤4 chars); auto = MC + 2 hex |
nodeinfo on/off | include NodeInfo (named node) — default on |
position on/off | include Position (map pin) — default on |
telemetry on/off | include Telemetry (battery + uptime) — default on |
presets / regions | list available values |
carnode — the car-specific park behaviour| Command | Effect |
|---|---|
carnode / status | drive state (nofix/driving/parked/sleeping/zone) + park config |
park <sec> | stopped time before an update fires (30–86400, default 300) |
radius <m> | movement within this counts as "stopped" (5–2000, default 30) |
advertdelay <sec> | gap from the Meshtastic burst to the MeshCore advert (0–600, default 10) |
sleep <hours> | parked this long → stop repeating until driving again (0–720, 0 = never, default 20) |
zone | list the quiet zones (up to 4) |
zone add <name> | quiet zone = the current spot; parking in it → repeat off immediately, radio quiet |
zone del <name> | forget that zone |
zone radius <name> <m> | how close to it counts (5–2000, default 100) |
home [clear|radius <m>] | shorthand for the zone named home |
mtbeacon on. The OLED home screen shows
CarNode driving / parked / sleeping / <zone name>.
Hold the board's user button for ~3 seconds and the node enters
its lowest-power state via board.powerOff().
sd_power_system_off); GPS is powered down. Wakes on the user button / reset.PlatformIO, from the
carnode branch
of the MeshCore-mtbeacon fork — the car node lives in
examples/car_node/.
pio run -e heltec_v4_carnode # Heltec V4 (ESP32-S3, SX1262)
pio run -e Heltec_t114_carnode # Heltec T114 (nRF52840, SX1262)
pio run -e t1000e_dualmode_carnode # Seeed T1000-E (nRF52840, LR1110, dual-mode)
The env extends the board's repeater and
adds the car-node add-on (the T114 one also sets -D ENV_INCLUDE_GPS=1):
[env:heltec_v4_carnode]
extends = env:heltec_v4_repeater
build_flags = ${env:heltec_v4_repeater.build_flags}
-D WITH_CAR_NODE
-D MT_HW_MODEL=110
-I examples/meshtastic_beacon
-I examples/car_node
lib_deps = ${env:heltec_v4_repeater.lib_deps}
Full details in the project README.
One-way presence only — it appears as a named node + map pin and
posts text, but does not route or rebroadcast traffic. A park floods a MeshCore
advert mesh-wide and adds airtime to a shared public Meshtastic channel, so
keep park reasonable, respect your region's limits, and don't
beacon onto channels you don't operate. The Meshtastic side defaults to
hops 0, so nothing it sends is rebroadcast by other people's
nodes.