From ff2f03d363d12336dce678605c765fc4be313837 Mon Sep 17 00:00:00 2001 From: agessaman Date: Wed, 18 Feb 2026 15:22:09 -0800 Subject: [PATCH] Enhance FAQ and path command configuration documentation - Added a new section in the FAQ detailing how to run meshcore-bot on a Raspberry Pi Zero 2 W, including specific configuration settings to manage memory usage effectively. - Updated the path command configuration documentation to clarify the default values and usage of `graph_startup_load_days` and `graph_capture_enabled`, improving user understanding of these settings. --- docs/faq.md | 25 +++++++++++++++++++++++++ docs/path-command-config.md | 11 +++++++++-- modules/mesh_graph.py | 13 ++++++++++++- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 344dd97..5a1876a 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -27,3 +27,28 @@ Without `--upgrade`, the script does *not* update the service file (systemd/laun ### How can I generate a custom command reference for my bot users? See [Custom command reference website](command-reference-website.md): it explains how to use `generate_website.py` to build a single-page HTML from your config (with optional styles) and upload it to your site. + +## Hardware and performance + +### How do I run meshcore-bot on a Raspberry Pi Zero 2 W? + +The Pi Zero 2 W has 512 MB of RAM, which is enough to run the bot, but the Mesh Graph can grow large on a busy mesh. Add the following to the `[Path_Command]` section of your `config.ini` to keep memory usage in check: + +```ini +[Path_Command] +# Limit startup memory: only load edges seen in the last 7 days. +# Edges older than this have near-zero path confidence anyway. +graph_startup_load_days = 7 + +# Evict edges from RAM after 7 days without a new observation. +graph_edge_expiration_days = 7 + +# Write graph updates in batches rather than on every packet. +graph_write_strategy = batched + +# If you don't use the !path command at all, disable graph capture +# entirely to eliminate the background thread and all graph overhead. +# graph_capture_enabled = false +``` + +These settings do not affect path prediction accuracy: edges older than a few days carry negligible confidence due to the 48-hour recency half-life used by the scoring algorithm. diff --git a/docs/path-command-config.md b/docs/path-command-config.md index 545706b..5691281 100644 --- a/docs/path-command-config.md +++ b/docs/path-command-config.md @@ -201,8 +201,15 @@ These settings control how graph edges are stored in the database. **`graph_startup_load_days`** (days, 0 = load all) - Load only edges seen in last N days on startup -- `0` = load all edges (recommended for development) -- Default: `0` +- `0` = load all edges (use on servers with ample RAM) +- Default: `14` (set to `0` in `config.ini` to load all) + +**`graph_capture_enabled`** (boolean) +- When `false`, no new edge data is collected from packets and the background + batch writer thread is not started — reducing CPU and RAM overhead +- Edges already in the database are still used for path validation +- Set to `false` on devices that don't use the path command +- Default: `true` ## Preset Configurations diff --git a/modules/mesh_graph.py b/modules/mesh_graph.py index 7934641..3833b1a 100644 --- a/modules/mesh_graph.py +++ b/modules/mesh_graph.py @@ -533,13 +533,24 @@ class MeshGraph: try: if is_new: - # Insert new edge + # Upsert new edge. + # Use INSERT ... ON CONFLICT DO UPDATE so that if the row already exists + # in the database (e.g. it was filtered out of the in-memory graph at + # startup by startup_load_days / edge_expiration_days, or written by a + # concurrent process), we merge rather than fail with UNIQUE constraint. query = ''' INSERT INTO mesh_connections (from_prefix, to_prefix, from_public_key, to_public_key, observation_count, first_seen, last_seen, avg_hop_position, geographic_distance) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(from_prefix, to_prefix) DO UPDATE SET + observation_count = MAX(observation_count, excluded.observation_count), + last_seen = MAX(last_seen, excluded.last_seen), + avg_hop_position = excluded.avg_hop_position, + geographic_distance = COALESCE(excluded.geographic_distance, geographic_distance), + from_public_key = COALESCE(excluded.from_public_key, from_public_key), + to_public_key = COALESCE(excluded.to_public_key, to_public_key) ''' params = ( edge['from_prefix'],