All settings live in config/default.yaml with user overrides in config/local.yaml. The service merges both files at startup: anything in local.yaml overrides the default. You only need to add the settings you want to change.
Edit your local config:
sudo nano /opt/meshpoint/config/local.yamlRestart after any config change: sudo systemctl restart meshpoint
radio:
region: "US" # US, EU_868, ANZ, IN, KR, SG_923
frequency_mhz: 906.875 # override within region's band limits
spreading_factor: 11 # 7-12. 11=LongFast, 9=MediumFast, 7=ShortFast/Turbo
bandwidth_khz: 250.0 # 125, 250, or 500
coding_rate: "4/5" # 4/5, 4/6, 4/7, 4/8
sync_word: 0x2B # 0x2B = Meshtastic. Don't change unless you know why.
preamble_length: 16 # 16 = Meshtastic standard
tx_power_dbm: 22 # SX1302 concentrator output power
spectral_scan_interval_seconds: 60 # noise floor sampler cadence (0 disables)
sx1261_spi_path: "" # SX1261 SPI device for spectral scan (empty = disabled)The region sets the base frequency, spreading factor, and bandwidth automatically. You only need region in most cases. Override frequency_mhz, spreading_factor, or bandwidth_khz individually to tune for non-default presets (MediumFast, ShortFast, etc.) or custom frequency slots.
| Region | Default frequency | Allowed band |
|---|---|---|
US |
906.875 MHz | 902.0 - 928.0 MHz |
EU_868 |
869.525 MHz | 863.0 - 870.0 MHz |
ANZ |
919.875 MHz | 915.0 - 928.0 MHz |
IN |
865.875 MHz | 865.0 - 867.0 MHz |
KR |
922.875 MHz | 920.0 - 923.0 MHz |
SG_923 |
917.875 MHz | 917.0 - 925.0 MHz |
If frequency_mhz falls outside the region's band limits, the service will reject it at startup. Omit frequency_mhz entirely to tune to the region default.
The dashboard's sidebar shows a live noise-floor reading. There are two ways the service can produce that number; which one applies depends on your concentrator hardware.
Hardware capability matrix:
| Carrier board | SX1261 reachable from Pi? | Spectral scan supported? |
|---|---|---|
| Semtech SX1302CXXXGW1 reference kit | yes (own SPI line) | yes |
| RAK2287 / RAK5146 / SenseCap M1 / most off-the-shelf concentrators | no (SX1261 is wired behind the SX1302's SPI router, not directly to the Pi) | no — packet-derived fallback only |
| Custom carriers with SX1261 on a dedicated CE line | yes | yes, after configuring sx1261_spi_path |
Default behaviour (works everywhere): sx1261_spi_path is empty, so the service skips the SX1261 init entirely and derives the noise floor from packet metadata — specifically a rolling minimum of RSSI − SNR across recently-decoded frames. This is a loose upper bound on the true noise floor (it tracks the quietest signal we managed to demodulate), but on a normally-operating link it converges to within a few dB of the real ambient floor and is good enough to spot RF interference, broken antennas, or unusually noisy bands.
Opting in to true spectral scan: if you have a board that exposes the SX1261 directly to the Pi (the Semtech reference kit is the common case), add this to config/local.yaml:
radio:
sx1261_spi_path: "/dev/spidev0.1"
spectral_scan_interval_seconds: 60When enabled, every spectral_scan_interval_seconds (default 60, minimum 5) the SX1261 samples ambient channel power on the radio's frequency for roughly 50 ms and the service reports the 10th-percentile reading as the noise floor. At 60 s cadence that is ~0.08% of receive time. If you set spectral_scan_interval_seconds: 0, scanning is disabled entirely and the packet-derived fallback is used.
Detecting an unsupported board. If you set sx1261_spi_path on a carrier where the SX1261 isn't directly reachable, libloragw will log lines like:
ERROR: sx1261_check_status: SX1261 status is not as expected: got:0x00 expected:0x22
ERROR: failed to patch sx1261 radio for LBT/Spectral Scan
…and lgw_start() may then refuse to bring up the concentrator. If you see that, revert sx1261_spi_path to "", restart the service, and stay on the packet-derived fallback.
If your libloragw build does not expose the spectral scan symbols at all (older HAL revisions), the service logs a single info line at startup and falls back automatically.
To match a Meshtastic preset, set spreading_factor and bandwidth_khz together:
| Preset | SF | BW (kHz) |
|---|---|---|
| ShortTurbo | 7 | 500 |
| ShortFast | 7 | 250 |
| ShortSlow | 8 | 250 |
| MediumFast | 9 | 250 |
| MediumSlow | 10 | 250 |
| LongFast (default) | 11 | 250 |
| LongModerate | 11 | 125 |
| LongSlow | 12 | 125 |
The dashboard's Configuration → Radio card includes a Custom chip alongside the named preset buttons. Selecting Custom reveals inputs for spreading factor (5-12), bandwidth (125 / 250 / 500 kHz), and coding rate (4/5, 4/6, 4/7, 4/8). Use it for combinations that don't appear in the named-preset table above (for example SF11 / BW125 / CR4/8 for an extra-resilient long-range link, or SF7 / BW500 / CR4/5 to mirror the deprecated ShortTurbo).
When current_preset in the radio configuration is empty (because
the saved SF / BW / CR doesn't match any named preset), the card
opens on Custom automatically and pre-fills the three inputs from
the values in local.yaml so you can see exactly what you're on.
Modem changes always require a service restart; the dashboard prompts you when one is needed. Off-spec combinations (anything not in the named table) may be silently dropped by neighboring nodes, so set the same Custom values on the receiving side too.
radio:
region: "EU_868"To also update your MeshCore companion radio:
meshpoint meshcore-radio EUOr enter a custom frequency: meshpoint meshcore-radio custom
See the Onboarding Guide for full details.
capture:
sources:
- concentrator # SX1302/SX1303 LoRa concentrator (RAK2287, etc.)
- meshcore_usb # optional MeshCore USB companion node
# - serial # optional plain Meshtastic USB node as a capture source
# - mock # optional synthetic packets for development
meshcore_usb:
auto_detect: true # scans /dev/ttyUSB* and /dev/ttyACM*
serial_port: null # or set explicitly: "/dev/ttyACM0"
baud_rate: 115200The setup wizard configures sources automatically. To add or remove a MeshCore companion later, edit sources and restart.
Available source types:
| Source | Purpose |
|---|---|
concentrator |
SX1302/SX1303 LoRa concentrator (RAK2287, RAK7248, SenseCap M1) |
meshcore_usb |
MeshCore USB companion node (Heltec V4, T-Beam, RAK4631 with MeshCore firmware) |
serial |
Plain Meshtastic node over USB serial. Used when you don't have a concentrator. |
mock |
Synthetic packet generator for development. Not for production. |
When running both Meshtastic concentrator capture and a MeshCore USB companion, pin meshcore_usb.serial_port explicitly. Auto-detect can grab the wrong device when multiple Espressif boards are attached.
location:
source: "static" # static | gpsd | uart
gpsd_host: "127.0.0.1" # gpsd TCP host (only when source=gpsd)
gpsd_port: 2947 # gpsd TCP port
update_interval_seconds: 5 # how often the coordinator polls the source
min_fix_quality: 1 # minimum NMEA fix quality (1=2D, 3=3D)location.source selects where the Meshpoint reads live GPS fixes
(for the Configuration → GPS skyplot and optional mesh POSITION
broadcasts). The setup wizard always writes static lat/lon/alt under
device.* as the registered Meshradar fleet pin (see
Device Identity); live gpsd does not overwrite
those values. Source changes require a service restart; registered
coordinates and mesh position settings hot-reload from the dashboard.
| Source | Behavior |
|---|---|
static (default) |
No live GPS hardware. Registered coordinates live in device.* only. Skyplot shows the static pin. |
gpsd |
Reads live fixes from the system gpsd daemon over TCP (127.0.0.1:2947). Recommended for any USB GPS receiver (u-blox 7, u-blox 8, VFAN puck, generic CDC ACM sticks). Skyplot and stats update from the live fix. |
uart |
Reserved for direct-serial reads from a Pi HAT GPS (e.g. RAK 7248). Currently a placeholder; falls back to static and surfaces an explanatory error in the dashboard. |
When native TX is enabled (transmit.enabled: true), the Meshpoint can
send periodic Meshtastic POSITION packets. That is separate from
the Meshradar fleet pin in device.*.
Configure on Configuration → GPS → Mesh position broadcasts, or in yaml:
transmit:
position:
interval_minutes: 15
startup_delay_seconds: 180
coordinate_source: "static" # static | live
location_precision: "approximate" # exact | approximate | none (live only)| Setting | Values | Meaning |
|---|---|---|
coordinate_source |
static (default) |
Broadcast the registered pin from device.latitude/longitude. |
live |
Broadcast the live gpsd/UART fix. Requires location.source other than static. |
|
location_precision |
approximate (default for live) |
Round to ~2 decimal places before POSITION TX (about 0.7 mi / 1.1 km; the dashboard label follows Settings → Meshpoint distance units). |
exact |
Full precision from the live fix. | |
none |
Skip POSITION broadcasts when using live (privacy: no position on mesh). |
When coordinate_source: static, coordinates are sent at full wizard
precision regardless of location_precision.
scripts/install.sh installs gpsd and gpsd-clients, configures
/etc/default/gpsd for USB hotplug (USBAUTO="true",
DEVICES="", GPSD_OPTIONS="-n"), and enables gpsd.socket. As
of v0.7.5 this happens on every fresh install and every
upgrade re-run.
To enable live GPS:
- Plug in a USB GPS receiver. udev rules shipped with
gpsdrecognize u-blox VIDs (0x1546) and auto-attach the device. The MeshCore USB auto-detect path (UsbPortClassifier) skips any port classified asgps_known, so a u-blox stick will never be probed as a MeshCore companion. - Open the dashboard at Configuration → GPS. Set Registered coordinates (Meshradar fleet pin). Switch Source to gpsd for live skyplot data. Optionally set Mesh position broadcasts to Live GPS with Approximate or Precise privacy, then click Save. Changing the GPS source type requires a service restart; coordinate and mesh-position edits hot-reload.
- Watch the GPS card. The skyplot animates, satellite dots render at their azimuth/elevation, and the fix-mode lamp flips from grey (no fix) → amber (2D) → green (3D) as the receiver acquires.
For headless / yaml-only setup add the section above to
local.yaml and restart the service. Verify with cgps (shipped
in gpsd-clients) or gpsmon.
| Receiver | Protocol | Tested |
|---|---|---|
| u-blox 7 USB stick | USB CDC ACM, NMEA + UBX | yes (RAK V2 .141) |
| u-blox 8 USB stick | USB CDC ACM, NMEA + UBX | yes |
| VFAN ublox 7 USB puck | USB CDC ACM, NMEA + UBX | yes |
RAK 7248 onboard u-blox via UART (/dev/ttyAMA0) |
NMEA over UART | placeholder (source: uart, not yet wired) |
Other USB receivers should work as long as gpsd recognizes the
device's VID. If cgps shows data but the dashboard does not,
check journalctl -u meshpoint | grep -i gpsd for connection
errors and confirm source: gpsd in local.yaml.
Three independent surfaces:
| Surface | Config | Notes |
|---|---|---|
| Meshradar cloud | device.latitude/longitude |
Always the registered pin. Live GPS never moves the fleet marker. |
| LoRa mesh (POSITION) | transmit.position.coordinate_source + location_precision |
Choose registered pin or live GPS; approximate / precise / hidden on live. |
| MQTT | mqtt.location_precision |
Applies to position fields in MQTT publishes only (exact / approximate / none). |
To run a mobile Meshpoint without leaking live coordinates on the mesh, use Live GPS with Hidden mesh privacy, or keep mesh source on Registered pin. To keep Meshradar on your home pin while testing gpsd outdoors, leave registered coordinates at home and use live mesh POSITION only if you intend to advertise on the LoRa map.
The primary (channel 0) name is used to compute the Meshtastic channel hash on transmitted packets. It must match the primary channel name on your mesh for outgoing messages to be heard.
meshtastic:
primary_channel_name: "LongFast"The default is LongFast (Meshtastic's standard public channel). Change it only if your mesh uses a custom primary channel name. You can also edit this from the dashboard: open the Radio tab, edit Channel 0, and save. The Radio and Messages tabs reflect the same value.
By default, the Meshpoint decrypts traffic on the standard Meshtastic default key (AQ==). To also decode packets on your private channels, add the channel keys to local.yaml:
meshtastic:
channel_keys:
MyChannel: "base64encodedPSK=="
AnotherChannel: "anotherBase64PSK=="Finding your channel's PSK: Open the Meshtastic app, go to the channel settings, and copy the pre-shared key (base64 format).
Channel name must match exactly what's configured on your Meshtastic node (case-sensitive).
The Meshpoint tries each configured key when decoding a packet. Packets matching any configured key will be fully decoded. Packets on channels with unknown keys will continue to show as ENCRYPTED.
To change the default Meshtastic key (if your primary channel uses a non-default PSK):
meshtastic:
default_key_b64: "yourPrimaryKeyBase64=="MeshCore uses its own default channel key, configurable separately:
meshcore:
default_key_b64: null # leave null to use the MeshCore built-in default
channel_keys:
SomeChannelName: "32-BytePSK" # Meshcore Channel Name with 32-Byte Hex PSK. One channel per line Any Channels listed in the YAML will show in the UI. Changes made in the UI will be written to the YAML config file and pushed to the USB Companion device. Additionally, all channels will be pushed to the USB Companion device upon Meshpoint startup. Maximum of 8 Channels can by configured at this time. THis limit may be adjusted at a later date.
The USB companion's advert name is what neighbors see in their contact list and on the mesh. As of v0.7.5 the dashboard owns the rename path:
- Configuration → MeshCore → Companion name edits the input,
ticks "Send advert after save" (default on), and clicks
Save Name. The Meshpoint sends
CMD_SET_ADVERT_NAMEto the companion (viameshcore.commands.set_name), persists the cleaned name tolocal.yamlundermeshcore.companion_name, and optionally fires an advert so neighbors pick up the new name immediately. - The configured name is re-applied on every USB reconnect. Hot-swapping a freshly-flashed companion, or replacing a failed unit, lands the new device on your configured name without a manual re-save.
meshcore:
companion_name: "Mesh Lab East" # optional. When set, re-applied on every USB reconnect.Leaving companion_name unset (the default) keeps the v0.7.4
behavior: the Meshpoint trusts whatever name is on the
companion's flash. Set it once from the dashboard; further
reboots / unplug / replug events re-apply automatically.
Validation (shared between the dashboard and the on-connect re-apply path): the name is stripped of leading/trailing whitespace, must not be empty, and must fit in 32 UTF-8 bytes (conservative cap matching the companion firmware's accepted range). 4-byte unicode codepoints (some emoji) count toward that limit.
Status: experimental — native onboard relay added in v0.7.4. When
transmit.enabled: truethe Meshpoint now relays through its own SX1302 concentrator using identity-preserving re-broadcast (originalsource_idandpacket_idsurvive, onlyhop_limitis decremented). No second radio required. Hardware validation is still in progress — please report results in Discord or via a Github issue.
Set both transmit and relay to enabled. The same SX1302 that handles outgoing messages re-broadcasts captured packets, sharing duty-cycle accounting so relay traffic can never crowd out user TX:
transmit:
enabled: true
# ... see Transmit (Native Messaging) below for the full block
relay:
enabled: true
max_relay_per_minute: 20
burst_size: 5
min_relay_rssi: -110.0
max_relay_rssi: -50.0
# serial_port intentionally omitted — native path is usedEncrypted packets (no key match locally) and MeshCore packets are intentionally skipped on the native path to avoid emitting garbage on the air.
The original v0.7.0–v0.7.3 path is preserved for setups that already have a second Meshtastic radio attached. Only used when transmit.enabled: false and relay.serial_port is set:
relay:
enabled: true
serial_port: "/dev/ttyACM1" # relay radio serial port
serial_baud: 115200
max_relay_per_minute: 20 # token-bucket rate limit
burst_size: 5 # max burst before throttle
min_relay_rssi: -110.0 # ignore weak packets
max_relay_rssi: -50.0 # ignore local packets (too strong)The relay path is independent from RX: transmission never blocks packet reception. Packets are deduplicated by ID, rate-limited, and filtered by signal strength before relay.
Enable the Meshpoint to send messages directly through the onboard SX1302 concentrator (Meshtastic) and the MeshCore USB companion (MeshCore). This powers the Messages tab on the local dashboard.
transmit:
enabled: false # opt-in
node_id: null # auto-generated 4-byte Meshtastic node ID
tx_power_dbm: 14 # conservative default (dBm)
# max_duty_cycle_percent omitted: auto-derives from radio.region
long_name: "Meshpoint"
short_name: "MPNT"
hop_limit: 3enabled: must be true to send from the Messages tab. Disabled by default.
node_id: leave as null to auto-generate. Once set, do not change it: your node identity is what other nodes see and cache in their contact lists.
tx_power_dbm: 14 dBm is conservative and compliant in most regions. Raise carefully; check your regional ISM band limits before increasing.
max_duty_cycle_percent: airtime limit as a percent of wall clock. Omit (or set to null) to auto-derive from radio.region: 10% in US/ANZ/KR/SG_923, 1% in EU_868/IN. Set explicitly in local.yaml to override (e.g. 25.0). See RADIO-CONFIG-EXPLAINED.md for the full table and rationale.
long_name / short_name: shown to other nodes (long name in node lists, short name on compact displays). Match your naming convention.
hop_limit: initial hop count on outgoing Meshtastic messages. 3 is typical; higher values mean more relays and more airtime.
MeshCore transmission uses the USB companion node: configure its serial port under capture.meshcore_usb (see Capture Sources above). The companion handles encryption and RF timing; the Meshpoint sends serial commands.
upstream:
enabled: true
url: "wss://api.meshradar.io"
reconnect_interval_seconds: 10
buffer_max_size: 5000 # local buffer during disconnects
auth_token: null # set by setup wizardWhen enabled, the Meshpoint connects to Meshradar via WebSocket and relays captured packets for aggregated mesh intelligence. The connection auto-reconnects with backoff and buffers packets locally during outages.
To run the Meshpoint without sending anything to the cloud, set:
upstream:
enabled: falseWhen enabled: false the Meshpoint never opens an upstream connection and never transmits any packet, heartbeat, or telemetry to meshradar.io. All capture, decoding, dashboard, MQTT, and storage features still work.
Note: the service still requires a valid
auth_tokento be present in your config at startup. Run the setup wizard once and paste the API key you received from Meshradar, then flipupstream.enabled: falseand operate fully offline. A standalone "no API key required" mode is on the backlog.
storage:
database_path: "data/concentrator.db"
max_packets_retained: 100000
cleanup_interval_seconds: 3600Packets are stored in a local SQLite database. Old packets are pruned automatically based on max_packets_retained.
dashboard:
host: "0.0.0.0" # listen on all interfaces
port: 8080
static_dir: "frontend"Access at http://<pi-ip>:8080. Bind to 127.0.0.1 to restrict to local access only.
Settings → System → Browser notifications enables LAN push alerts while any authenticated dashboard tab is open. Preferences are stored in the browser (localStorage), not on the device.
Supported alert types:
- Node offline — not heard for 2+ hours (matches the node-card online dot)
- Node back online — heard again after being offline
- Low battery — telemetry reports ≤20% (one alert per node per hour)
- Storm guard — reserved for a future release; toggle is present but inactive until storm-guard quarantine ships
Grant notification permission when prompted. Use Suppress when this tab is focused to avoid duplicate toasts while you are actively watching the dashboard.
device:
device_name: "My Meshpoint"
latitude: 40.7128
longitude: -74.0060
altitude: 25Set during the setup wizard. The coordinates are used for map placement on the local dashboard and the Meshradar cloud dashboard, and as the reference point for "farthest direct node" distance.
Three options:
-
Configuration → GPS in the dashboard (recommended). Edit lat/lon/alt for
source: static, or switch tosource: gpsdto consume live fixes from a USB GPS receiver. See Location (GPS) source above. -
Edit
local.yamldirectly (fastest for headless tweaks):sudo nano /opt/meshpoint/config/local.yaml # change device.latitude / device.longitude / device.altitude sudo systemctl restart meshpoint -
Re-run the setup wizard and press Enter through steps you want to keep:
sudo /opt/meshpoint/venv/bin/python -m src.cli setup sudo systemctl restart meshpoint
Tip: in Google Maps, right-click any location and click the coordinates at the top of the menu to copy them in decimal format (e.g. 40.7128, -74.0060).
Publish captured packets to community MQTT brokers (meshmap.net, NHmesh.live, etc.) and Home Assistant. The Meshpoint acts as a dual-protocol MQTT gateway: both Meshtastic and MeshCore traffic can be published from a single device.
MQTT publishing uses two independent safety gates to prevent accidental exposure of private data:
Gate 1: Global kill switch. MQTT is off by default. You must explicitly set mqtt.enabled: true to activate publishing. Nothing is ever sent to any MQTT broker unless you opt in.
Gate 2: Channel allowlist. Only packets from channels listed in publish_channels are published. The default list contains only LongFast (the standard Meshtastic public channel). Private channels, custom PSK channels, and encrypted packets are never published unless you deliberately add that channel name to the list.
Both gates must pass for any packet to leave the device via MQTT. Encrypted packets (those the Meshpoint could not decrypt) are always blocked regardless of channel configuration.
This two-gate approach is informed by active community discussion around MQTT privacy, including the need for explicit opt-in controls (meshtastic/firmware#5507), concerns about private channel data leaking via MQTT gateways (meshtastic/firmware#5404), and the broader push for user-controlled MQTT publishing (meshtastic/firmware#3549).
mqtt:
enabled: true
broker: "mqtt.meshtastic.org"
port: 1883
username: "meshdev"
password: "large4cats"
region: "US"
publish_channels:
- "LongFast"This publishes standard Meshtastic and MeshCore traffic to the community broker. Your Meshpoint appears on community maps (meshmap.net, Liam Cottle, NHmesh) with a unique gateway ID that integrates natively with the Meshtastic ecosystem.
mqtt:
enabled: false # Gate 1: must be true to publish
broker: "mqtt.meshtastic.org" # broker hostname
port: 1883 # broker port
username: "meshdev" # broker credentials
password: "large4cats"
topic_root: "msh" # MQTT topic prefix
region: "US" # used in topic path
publish_channels: # Gate 2: only these channels are published
- "LongFast"
publish_json: false # also publish JSON on /json/ topic
location_precision: "exact" # exact | approximate | none
homeassistant_discovery: false # publish HA auto-discovery configsConfiguration → MQTT does not expose TLS/mqtts settings yet. The publisher uses plain TCP; port 8883 alone does not enable encryption.
Planned: ship broker TLS (tls_enabled, optional CA file) in the same
update as Meshtastic PKI (see ROADMAP.md in the private repo). Until
then:
- Public Meshtastic MQTT:
mqtt.meshtastic.orgon port 1883 (default). - Private TLS brokers: wait for that release, or terminate TLS on a local reverse proxy in front of a plain MQTT listener.
This is separate from packet privacy: undecrypted LoRa packets are never published to MQTT regardless of transport settings.
Control how much location detail leaves the device via MQTT:
| Value | Behavior |
|---|---|
exact |
Full GPS coordinates (default) |
approximate |
Rounded to ~2 decimal places (about 0.7 mi / 1.1 km; Configuration → MQTT and GPS labels follow Settings → Meshpoint distance units) |
none |
Location stripped entirely from MQTT messages |
Full-precision location data is always available on the Meshradar dashboard regardless of this setting.
Enable JSON publishing and HA auto-discovery to automatically create sensors in Home Assistant for battery level, temperature, and GPS position of mesh nodes:
mqtt:
enabled: true
publish_json: true
homeassistant_discovery: trueHA sensors appear as sensor.meshpoint_<node_id>_battery, sensor.meshpoint_<node_id>_temperature, and device_tracker.meshpoint_<node_id>.
If you want to publish traffic from a private channel (for example, to feed it into your own HA instance on a local broker), add the channel name to publish_channels and point the broker to your local MQTT server:
mqtt:
enabled: true
broker: "192.168.1.100" # your local broker
username: ""
password: ""
publish_channels:
- "LongFast"
- "MyPrivateChannel" # explicitly opted inNever add private channel names when publishing to a public broker.
See config/default.yaml for all available settings and their defaults.
A flat overview of every top-level section in local.yaml. Use this as a checklist when assembling a custom config.
device: # name, location, firmware version (mostly wizard-managed)
device_id: null
device_name: "My Meshpoint"
firmware_version: "0.6.5"
latitude: null
longitude: null
altitude: null
radio: # LoRa physical layer
region: "US"
frequency_mhz: 906.875
spreading_factor: 11
bandwidth_khz: 250.0
coding_rate: "4/5"
sync_word: 0x2B
preamble_length: 16
tx_power_dbm: 22
spectral_scan_interval_seconds: 60 # noise floor sampler; 0 disables
meshtastic: # Meshtastic protocol settings
primary_channel_name: "LongFast"
default_key_b64: "1PG7OiApB1nwvP+rz05pAQ=="
channel_keys: {}
decode_timeout_ms: 100
meshcore: # MeshCore protocol settings
default_key_b64: null
channel_keys: {}
companion_name: null # Optional. When set, re-applied on every USB reconnect.
capture: # what packet sources to read from
sources:
- concentrator
- meshcore_usb
meshcore_usb:
auto_detect: true
serial_port: null
baud_rate: 115200
location: # GPS / location source
source: "static" # static | gpsd | uart
gpsd_host: "127.0.0.1"
gpsd_port: 2947
update_interval_seconds: 5
min_fix_quality: 1
transmit: # native messaging TX (Meshtastic via SX1302, MeshCore via USB)
enabled: false
node_id: null
tx_power_dbm: 14
# max_duty_cycle_percent omitted: auto-derives from radio.region
long_name: "Meshpoint"
short_name: "MPNT"
hop_limit: 3
position:
interval_minutes: 15
coordinate_source: "static" # static | live
location_precision: "approximate" # exact | approximate | none (live only)
relay: # experimental: re-broadcast captured packets via USB radio
enabled: false
serial_port: "/dev/ttyACM1"
serial_baud: 115200
max_relay_per_minute: 20
burst_size: 5
min_relay_rssi: -110.0
max_relay_rssi: -50.0
upstream: # cloud (Meshradar) connection
enabled: true
url: "wss://api.meshradar.io"
reconnect_interval_seconds: 10
buffer_max_size: 5000
auth_token: null # required at startup, set by setup wizard
mqtt: # MQTT publishing (off by default)
enabled: false
broker: "mqtt.meshtastic.org"
port: 1883
username: "meshdev"
password: "large4cats"
topic_root: "msh"
region: "US"
publish_channels:
- "LongFast"
publish_json: false
location_precision: "exact"
homeassistant_discovery: false
storage: # local SQLite packet store
database_path: "data/concentrator.db"
max_packets_retained: 100000
cleanup_interval_seconds: 3600
dashboard: # local web UI
host: "0.0.0.0"
port: 8080
static_dir: "frontend"You only need to put the keys you want to override into local.yaml. Every key omitted from local.yaml falls back to the value in config/default.yaml.