MeshCore fork

MeshCore Car Node

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.

Boards: Heltec V4 · Heltec T114 · Seeed T1000-E Firmware v1.16.0+carnode Build 4769627a · 22 Jul 2026

# What it is

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.

mtbeaconcar node
Position sourceconfigured lat/lon (static)live GPS fix at park time
Triggerfixed interval (minutes)vehicle parks (stopped > park sec) + light presence every interval min
Networks updatedMeshtastic onlyMeshtastic + MeshCore together
While drivingbeacons periodicallypresence only (NodeInfo, no position) at interval
CLI verbmtbeacon …carnode …
Config file/mtbeacon/carnode

🅿️ Park-triggered

Location updates fire once the GPS fix sits still for park seconds (default 5 min); a light presence keeps the node visible in between.

🔗 Both maps at once

The same fix updates the Meshtastic beacon and floods a fresh MeshCore advert in the same event.

🛰️ One-way presence

Appears as a named node + map pin and optional text. Does not route or rebroadcast traffic.

😴 Hold-to-hibernate

Hold the user button ~3 s to drop the node into its lowest-power state; tap to wake.

🏠 Quiet zones

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.

🛡️ Self-recovering

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.

✅ Confirmed transmits

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.

🔋 Battery on the map

Each burst carries Meshtastic telemetry, so a car parked for days shows its charge draining in any Meshtastic client.

# The park model

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:

  1. a Meshtastic beacon burst — NodeInfo + Position (+ text), and
  2. a MeshCore update — the repeater writes the parked fix into its NodePrefs location and floods a fresh advert, so the whole MeshCore mesh learns where you stopped.
Both use the same fix from one park event (Meshtastic first, then the MeshCore advert ~10 s later so they don't collide). Re-parking in the same spot won't re-broadcast (de-duped by radius); mtbeacon send forces an update immediately. The MeshCore location is persisted, so it survives a reboot while parked.
Jitter-proof: the park logic — and the broadcast position — uses the median of the last ~9 fixes (~27 s window), so a single fix teleporting hundreds of metres (trunk-mounted GPS, multipath) is rejected outright; movement must then persist outside 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.
Periodic presence: between park events a light presence beacon fires every 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.
Chat text: the 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.
Repeat sleep: after the vehicle has sat still for 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.
Suppression never touches your settings. The sleep and quiet-zone states are layered over the 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.

Builds before 4769627a wrote the pref directly. Because MeshCore persists all settings as one block, changing any setting while parked in a quiet zone saved the suppressed value — and the wake path then read it back as a deliberate choice and never restored forwarding. If a node has gone quiet, run set repeat on once while parked outside any zone; it clears for good.
Quiet zones: park where the vehicle usually lives and say 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.
Hop limit: Meshtastic packets go out at 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.
If the mesh is busy: the two halves of a park are independent once armed. When the repeater can't queue the MeshCore advert — its packet pool or send queue full while forwarding a burst — the node retries the handoff with a backoff instead of losing the park silently, and leaves the periodic advert timer alone so the next scheduled advert still carries the location. carnode status shows adv!N if any park re-advert was abandoned outright; normally it's absent.

# Download firmware

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.

Heltec V4 ESP32-S3 · SX1262

  • New / erased board (or boot loops with invalid header: 0xffffff): flash the -full image at 0x0 — it bundles the bootloader + partition table.
  • Updating a board already running this firmware: flash the app-only .bin at 0x10000.
  • Button-only hibernate (stays asleep through LoRa traffic).

esptool.py write_flash 0x0 heltec_v4_carnode-4769627a-full.bin  # fresh board

esptool.py write_flash 0x10000 heltec_v4_carnode-4769627a.bin  # update

Heltec T114 nRF52840 · SX1262

  • DFU .zip via adafruit-nrfutil over serial, or flash the .hex with a debugger.
  • True system-off hibernate (GPS powered down).

adafruit-nrfutil dfu serial -pkg heltec_t114_carnode-dfu.zip -p /dev/ttyACM0 -b 115200

Seeed T1000-E nRF52840 · LR1110 · dual-mode

  • Dual-mode: pressing the button 5x reboots between the car node (repeater mode) and a normal phone-paired companion node. 2 beeps = repeater, 1 beep = companion.
  • Onboard GPS — no external hardware. Ships in companion mode; 5x press to start the car node.
  • Easiest flash: double-tap the reset button, then drag the .uf2 onto the T1000-E USB drive that appears (keeps the stock bootloader).
  • System-off hibernate (~3 s hold in repeater mode; GPS powered down).

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.

# Runtime control

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)

CommandEffect
mtbeacon / statusshow beacon RF config
stats / stats cleartransmit health since boot / reset the counters
on / offenable / disable beaconing
sendpush 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/offinclude NodeInfo (named node) — default on
position on/offinclude Position (map pin) — default on
telemetry on/offinclude Telemetry (battery + uptime) — default on
presets / regionslist available values

🅿️ carnode — the car-specific park behaviour

CommandEffect
carnode / statusdrive 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)
zonelist 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
Defaults: US LongFast · 5-min park / 30 m radius · 30-min presence · 22 dBm (region-capped) · 20 h repeat sleep · disabled until mtbeacon on. The OLED home screen shows CarNode driving / parked / sleeping / <zone name>.

# Hibernate — hold the user button (~3 s)

Hold the board's user button for ~3 seconds and the node enters its lowest-power state via board.powerOff().

On the V4 the user button is GPIO0 (the BOOT strapping pin), so the firmware waits for you to release it before sleeping, and on wake a brief tap is safest — holding GPIO0 down through the reset can drop the chip into serial-download mode instead of booting.

# Build from source

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.

# Scope & etiquette

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.