Files
meshcore-bot/config.ini.pi2-example
agessaman 99e976eca0 feat: Improve ARMv7 installation process with piwheels support
- Updated `install-service.sh` to source `scripts/armv7_pip_args.sh`, ensuring consistent handling of pip arguments for 32-bit ARM hosts.
- Enhanced `constraints-armv7.txt` to clarify the need for specific package versions to avoid installation issues (issue #269).
- Modified `README.md` to document the application of piwheels index and constraints during installation on 32-bit ARM systems.
- Added tests to verify the correct integration of the ARMv7 pip arguments helper and its behavior across different architectures.
2026-09-05 08:46:05 -07:00

193 lines
12 KiB
Plaintext

####################################################################################################
# #
# 32-bit Raspberry Pi Tuning Profile (Pi 2 / Pi 3 armhf) #
# #
# THIS IS AN OVERLAY, NOT A COMPLETE CONFIG. It contains only the keys that should differ from #
# their defaults on a memory- and I/O-constrained 32-bit Pi. Start from one of the complete #
# configs and copy these sections over the matching ones: #
# #
# config.ini.quickstart - smallest working config #
# config.ini.minimal-example - core testing commands only #
# config.ini.example - every option, fully documented #
# #
# Every key here also exists in config.ini.example with its full description. This file explains #
# only WHY the value differs for a small Pi. #
# #
# Target hardware: Raspberry Pi 2 Model B (2015, BCM2836 / Cortex-A7 / ARMv7 / 1 GB RAM) and #
# similar 32-bit armhf boards. Also useful on a Pi 3 running a 32-bit OS. #
# #
####################################################################################################
####################################################################################################
# DEPLOYMENT NOTES (read before you build)
####################################################################################################
#
# ARCHITECTURE
# A 2015 Pi 2 Model B is ARMv7 (Cortex-A7), which is exactly what Debian armhf and the project's
# linux/arm/v7 image target. The published image runs there unmodified - no Dockerfile changes
# are needed. Verified: uname -m = armv7l, Python 3.11, all runtime dependencies import.
#
# Check your board first: cat /proc/cpuinfo | grep -i revision
# A Pi 2 v1.2 (late 2016) is BCM2837 / Cortex-A53, which is 64-bit capable. If you have that
# revision, install a 64-bit OS and use the linux/arm64 image instead - it avoids every 32-bit
# caveat below and is a fully supported Docker platform.
#
# DOCKER ENGINE VERSION CEILING
# Docker Engine v28 is the LAST major version to ship packages for 32-bit Raspberry Pi OS
# (armhf). v29 and later provide no armhf packages. On a 32-bit Pi you must pin to v28.x and
# accept that the engine will not receive further major updates. A true Pi 2 v1.1 cannot run a
# 64-bit OS, so there is no upgrade path off this ceiling short of new hardware.
#
# DEPENDENCIES / BUILDING
# Ten dependencies publish no prebuilt armv7 wheel on PyPI and would compile from source:
# PyNaCl (bundles libsodium), pycryptodome, ephem, pyephem, brotli, cffi, MarkupSafe, librt,
# backports.zstd, sgmllib3k. On a 900 MHz Cortex-A7 with 1 GB RAM that is hours of work and a
# likely OOM. Everything else already resolves to a wheel - cryptography, aiohttp, jellyfish
# and qh3 all publish armv7 builds, so no Rust toolchain is needed.
#
# You should not have to compile any of them:
#
# Docker install - nothing to do. The published linux/arm/v7 image ships everything
# prebuilt. Just pull it; do not build the image on the Pi.
#
# Native install - install-service.sh and the .deb postinst handle this automatically.
# On a 32-bit ARM host they add the piwheels index and apply constraints-armv7.txt,
# which resolves the entire dependency set to wheels (measured: 75/75, zero compiled).
# Raspberry Pi OS already configures piwheels in /etc/pip.conf; the explicit flag also
# covers Ubuntu armhf and DietPi, which do not. Without the constraints file, pip can
# pick a listed-but-undownloadable brotli 1.2.0 wheel and abort (issue #269).
#
# Manual pip install - pass both yourself:
# pip install --extra-index-url https://www.piwheels.org/simple \
# -c constraints-armv7.txt -r requirements.txt
#
# See constraints-armv7.txt for the measurements and for how to re-check the pins as
# piwheels catches up.
#
# RADIO TRANSPORT
# The Pi 2 has no onboard Bluetooth or WiFi - those arrived with the Pi 3. BLE requires a USB
# dongle plus host BlueZ/D-Bus passthrough into the container. USB serial is the simpler and
# more reliable path here; see the devices: stanza in docker-compose.yml.
#
# STORAGE
# Database I/O on an SD card is the real bottleneck on this class of hardware, which is what the
# retention settings below address. Putting the database on USB storage rather than the SD card
# helps more than any single config value.
#
####################################################################################################
####################################################################################################
# ONE-TIME CLEANUP (only relevant when migrating an existing database)
####################################################################################################
#
# A fresh database does not need this. If you are moving an existing database onto a Pi, two items
# are not governed by retention and will not shrink on their own:
#
# 1. topology_inference_shadow - an orphaned table from removed shadow-comparison work. It has no
# remaining references anywhere in the codebase and is no longer written. On the reference
# deployment it held 248k rows / 130 MB of dead weight.
#
# sqlite3 meshcore_bot.db "DROP TABLE IF EXISTS topology_inference_shadow;"
#
# Back up the database first (backup_database.py) and confirm the table is stale in YOUR
# database before dropping it:
# sqlite3 meshcore_bot.db "SELECT COUNT(*), MAX(created_at) FROM topology_inference_shadow;"
#
# 2. Free pages left behind by past deletions. VACUUM reclaims them, but it rewrites the whole
# file - run it off-device or during a maintenance window, not on the Pi mid-flight:
#
# sqlite3 meshcore_bot.db "VACUUM;"
#
# Note that a fresh database is NOT a substitute for the retention values below. Retention is
# enforced correctly, so database size converges to a steady state set by these windows and your
# mesh's traffic volume. Starting fresh delays that convergence, it does not avoid it.
#
####################################################################################################
[Data_Retention]
# This is the section that matters most on a small Pi. Sizes below were measured on a reference
# deployment on a busy mesh, with every table sitting exactly at its retention window. Your
# absolute numbers scale with local traffic volume, but the ratios between tables hold.
#
# table (incl. indexes) default window steady state growth
# ------------------------- ---------------- -------------- ------------
# observed_paths 90 days 562 MB 6.2 MB/day
# packet_stream 3 days 298 MB 99.4 MB/day
# unique_advert_packets 90 days 113 MB 1.3 MB/day
# mesh_connections 7 days 12 MB 1.7 MB/day
#
# The defaults total roughly 1.2 GB in steady state. The values below bring that to roughly 300 MB
# and hold it there.
# Deletion batching. The shipped defaults were already chosen for Pi/SD-card installs; smaller
# batches and a longer pause reduce I/O bursts at the cost of draining a backlog more slowly.
# Worth lowering only if you see the bot stutter during the daily cleanup pass.
retention_delete_batch_size = 500
retention_delete_pause_seconds = 0.25
# Packet stream. By far the densest writer in the database - roughly 99 MB/day against
# observed_paths' 6 MB/day. On a 32-bit Pi this is both the capacity concern and the SD-card
# write-endurance concern. 1 day keeps the web viewer's real-time display working while cutting
# the largest single source of write churn.
# Raise to 2 if you want a bit more history and can afford ~100 MB more.
packet_stream_retention_days = 1
# Repeater/stats tables. daily_stats_retention_days governs both daily_stats and
# unique_advert_packets; observed_paths_retention_days governs observed_paths.
# observed_paths plus its six indexes is the single largest consumer at the default 90 days
# (562 MB); 14 days brings it to roughly 87 MB with no loss of recent-path accuracy.
daily_stats_retention_days = 30
observed_paths_retention_days = 14
# Purging log - a small audit trail, but there is no reason to keep a quarter of it on a Pi.
purging_log_retention_days = 30
# Mesh connections (path graph edges). Left at the default deliberately. This table is small
# (12 MB including indexes) and is what the path/trace commands and the mesh page read.
# Must stay >= Path_Command graph_edge_expiration_days.
mesh_connections_retention_days = 7
# Zero-hop neighbour history. At most one row per neighbour per cycle and cycles are >= 12h apart,
# so this grows very slowly and the long default window is cheap even here. Left at the default.
neighbor_observations_retention_days = 365
[Path_Command]
# Mesh graph tuning.
#
# The mesh graph is a common suspect for Pi resource pressure, but on the reference deployment it
# is not the problem: 12 MB of database including indexes (about 1% of total) and 16 MB resident
# for 31k edges. It is already bounded to 7 days and already batches its writes specifically to
# keep WAL churn low on SD cards. Disabling it saves very little and costs you the path command,
# trace edge updates, neighbors_feed_mesh_graph, and the mesh page.
#
# The recommendation for a 32-bit Pi is therefore to KEEP capture enabled and simply narrow the
# startup load, rather than turning the feature off.
# Leave capture on. See the two traps below before changing this.
graph_capture_enabled = true
# Batched writes keep transaction and WAL churn low - the right choice on SD cards.
# Do not use 'hybrid' here; it persists every new edge immediately.
graph_write_strategy = batched
graph_batch_interval_seconds = 60
# How many days of edges to load into memory at startup.
#
# TRAP: 0 does NOT mean "load nothing" - it means "no window". The loader builds its filter as
# [days for days in (graph_startup_load_days, graph_edge_expiration_days) if days > 0]
# and applies the smallest active window, so setting BOTH of these to 0 removes the WHERE clause
# entirely and loads every edge ever recorded. Always use a small positive number here.
graph_startup_load_days = 2
# Edges older than this are never loaded and are pruned from the database.
# Keep <= Data_Retention mesh_connections_retention_days.
graph_edge_expiration_days = 7
# TRAP: setting graph_capture_enabled = false does NOT reclaim the startup memory. MeshGraph
# calls _load_from_database() unconditionally in __init__, before it checks capture_enabled, so
# you still pay the full startup query and the in-memory graph. Disabling capture stops new edge
# writes and the batch-writer thread - nothing more. If your goal is memory, lower
# graph_startup_load_days instead; that is the knob that actually changes the footprint.