Passer au contenu

Bitcoin accepté au paiement  |  Expédié depuis Montréal, QC, Canada  |  Soutien expert depuis 2016

Store & Forward Meshtastic : mémoire tampon hors réseau

Réponse rapide

Store & Forward (S&F) est le module Meshtastic qui permet à un nœud bien placé de conserver l’historique des messages texte du mesh, pour que les nœuds éteints ou hors de portée puissent demander une relecture à leur retour. Un serveur (un appareil ESP32 en rôle ROUTER/ROUTER_LATE, ou avec is_server activé, disposant d’au moins 1 Mo de PSRAM libre) stocke chaque message texte qu’il entend; un client demande l’historique sur le port STORE_FORWARD_APP (65) et reçoit par défaut jusqu’à 25 messages stockés des 240 dernières minutes, cadencés à un paquet par 5000 ms. Chaque nombre de cette page est cité du code source du micrologiciel.

Télécharger le CSV Télécharger le JSON API REST →

Transcrit mot à mot depuis meshtastic/firmware → src/modules/StoreForwardModule.cpp / .h et meshtastic/protobufs → storeforward.proto / module_config.proto / portnums.proto (en date du 2026-07-22). Les valeurs par défaut, les seuils et les valeurs d’énumération sont des littéraux exacts de ces fichiers; les lignes du tableau et les fichiers de données restent en anglais parce que ce sont des citations littérales des sources. Notez que portnums.proto marque toujours STORE_FORWARD_APP « (Work in Progress) » — le comportement peut changer entre les versions du micrologiciel; la date ci-dessus fait foi.

Comment fonctionne le Store & Forward

  • Un nœud devient la mémoire du mesh. Un nœud capable d’agir comme serveur (ESP32 avec PSRAM, en rôle ROUTER/ROUTER_LATE ou avec is_server activé) écoute tout le trafic et copie chaque message texte entendu dans un tampon circulaire en PSRAM (historyAdd()). Les positions et la télémétrie ne sont pas stockées — wantPacket() n’accepte que TEXT_MESSAGE_APP et STORE_FORWARD_APP.
  • La capacité est dimensionnée d’après la PSRAM. À moins que records soit défini, le tampon est dimensionné à (((getFreePsram() / 4) * 3) / sizeof(PacketHistoryStruct)) — jusqu’aux trois quarts de la PSRAM libre. Une fois plein, le serveur journalise « S&F - PSRAM Full. Starting overwrite » et boucle en écrasant les enregistrements les plus anciens.
  • Les clients demandent une relecture. Un client envoie CLIENT_HISTORY (avec, en option, une window en minutes) sur le port 65; sans fenêtre, le serveur applique son défaut de 240 minutes. Le serveur répond ROUTER_HISTORY (combien de messages arrivent, la fenêtre utilisée, et last_request pour que les requêtes répétées sautent les paquets déjà livrés), puis rejoue chaque message stocké en ROUTER_TEXT_BROADCAST ou ROUTER_TEXT_DIRECT.
  • La relecture est polie par conception. Au plus historyReturnMax (25 par défaut) messages par requête, un paquet par packetTimeMax (5000 ms), et seulement tant que la barrière d’utilisation du canal le permet (le commentaire du code la plafonne à « moins de 25 % d’utilisation »). Les paquets rejoués partent en priorité BACKGROUND avec want_ack = false.
  • Le filtre de relecture protège les frontières de vie privée. Un client ne reçoit que des diffusions ou des messages directs qui lui sont adressés — jamais ses propres messages ni les messages directs des autres — et les demandes d’historique sur le canal public par défaut sont refusées d’emblée.

Chaque réglage, valeur par défaut et code de protocole

GroupeÉlémentValeur / nºUnité / typeSourceDescription (en anglais — littérale)
Requirement Supported architectures ARCH_ESP32, ARCH_PORTDUINO compile guard StoreForwardModule.cpp The whole module body is wrapped in "#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)" — S&F only exists on ESP32 devices and the Linux-native (Portduino) build. nRF52 devices cannot run it.
Requirement Server role gate ROUTER, ROUTER_LATE, or is_server device role StoreForwardModule.cpp Server mode initializes only when config.device.role is ROUTER or ROUTER_LATE, or moduleConfig.store_forward.is_server is set. Every other enabled node becomes a client (is_client = true).
Requirement PSRAM presence memGet.getPsramSize() > 0 condition StoreForwardModule.cpp A server-mode device without PSRAM logs "S&F: device doesn't have PSRAM, Disable" and disables the module.
Requirement Minimum free PSRAM 1024 * 1024 bytes (1 MB) StoreForwardModule.cpp Server startup requires memGet.getFreePsram() >= 1024 * 1024; below that it logs "S&F: not enough PSRAM free, Disable".
Firmware default historyReturnMax 25 records StoreForwardModule.h "Return maximum of 25 records by default." — the most messages a server will replay per history request unless history_return_max overrides it.
Firmware default historyReturnWindow 240 minutes (4 h) StoreForwardModule.h "Return history of last 4 hours by default." — the replay window used when a client does not specify one, unless history_return_window overrides it.
Firmware default records 0 (auto-calculated) records StoreForwardModule.h Defaults to 0, meaning populatePSRAM() computes capacity as (((memGet.getFreePsram() / 4) * 3) / sizeof(PacketHistoryStruct)) — up to 3/4 of free PSRAM.
Firmware default heartbeat false bool StoreForwardModule.h "No heartbeat." — the server broadcasts no heartbeat unless moduleConfig.store_forward.heartbeat enables it.
Firmware default heartbeatInterval 900 seconds (15 min) StoreForwardModule.h When heartbeat is enabled, the server broadcasts a ROUTER_HEARTBEAT every heartbeatInterval seconds (checked as heartbeatInterval * 1000 ms); the period is echoed in variant.heartbeat.period.
Firmware default packetTimeMax 5000 ms StoreForwardModule.h "Interval between sending history packets as a server." — runOnce() paces replay so stored messages go out at most one per 5 seconds.
Firmware behaviour Channel-utilization gate isTxAllowedChannelUtil(true) condition StoreForwardModule.cpp Replay and heartbeat transmissions only happen when airtime permits: "Only send packets if the channel is less than 25% utilized and until historyReturnMax".
Firmware behaviour Stored payload types TEXT_MESSAGE_APP only PortNum StoreForwardModule.cpp historyAdd() runs on TEXT_MESSAGE_APP packets — the history holds text messages, not positions or telemetry. wantPacket() accepts only TEXT_MESSAGE_APP and STORE_FORWARD_APP.
Firmware behaviour History buffer overwrite wraps to 0 when full ring buffer StoreForwardModule.cpp When packetHistoryTotalCount reaches records the server logs "S&F - PSRAM Full. Starting overwrite", resets the counter to 0 and starts overwriting the oldest slots.
Firmware behaviour Replay filter from != dest && (to == BROADCAST || to == dest) condition StoreForwardModule.cpp A client is only sent packets it did not author itself, and only broadcasts or direct messages addressed to it.
Firmware behaviour Public-channel refusal "S&F not permitted on the public channel." text reply StoreForwardModule.cpp History requests arriving on the default (public) channel are refused with this text message (channels.isDefaultChannel check).
Firmware behaviour Busy refusal "S&F - Busy. Try again shortly." text reply StoreForwardModule.cpp While the server is replaying to one client it answers other history requests with this text message; CLIENT_STATS gets a ROUTER_BUSY instead.
Firmware behaviour Client retry delay available_packets * packetTimeMax * (2 if ROUTER_ERROR else 1) ms StoreForwardModule.cpp On ROUTER_BUSY / ROUTER_ERROR a client computes retry_delay = millis() + getNumAvailablePackets(...) * packetTimeMax, doubled for ROUTER_ERROR.
Firmware behaviour Legacy text trigger "SF" + 0x00 text message StoreForwardModule.cpp A direct text message whose payload starts with bytes 'S', 'F', 0x00 is a "Legacy Request to send" — the server replays historyReturnWindow * 60 seconds of history to the sender.
Firmware behaviour Replay want_ack false bool StoreForwardModule.cpp Replayed and protocol packets are sent with want_ack = false and priority BACKGROUND: "Let's assume that if the server received the S&F request that the client is in range."
Config field enabled 1 bool module_config.proto "Enable the Store and Forward Module" — off by default, like other Meshtastic modules.
Config field heartbeat 2 bool module_config.proto Enables the periodic ROUTER_HEARTBEAT broadcast on a server (firmware default: off).
Config field records 3 uint32 module_config.proto Maximum number of records to store in memory; 0 lets the firmware auto-size from free PSRAM.
Config field history_return_max 4 uint32 module_config.proto Overrides the maximum number of records returned per history request (firmware default 25).
Config field history_return_window 5 uint32 module_config.proto Overrides the history window in minutes (firmware default 240 = 4 hours).
Config field is_server 6 bool module_config.proto "Set to true to let this node act as a server that stores received messages and resends them upon request." — lets a non-router role run the server.
Protocol STORE_FORWARD_APP 65 PortNum portnums.proto The S&F protocol port — flagged "(Work in Progress)" in portnums.proto. See the PortNum registry.
RequestResponse UNSET 0 enum storeforward.proto Unset/unused.
RequestResponse ROUTER_ERROR 1 enum storeforward.proto Router is in an error state. Codes 001–063 are from the router.
RequestResponse ROUTER_HEARTBEAT 2 enum storeforward.proto Router heartbeat — carries a Heartbeat variant with period (seconds) and secondary (0 = primary router).
RequestResponse ROUTER_PING 3 enum storeforward.proto Router has requested the client respond; works as an "are you there" message.
RequestResponse ROUTER_PONG 4 enum storeforward.proto The response to a "Ping". A client treats it like receiving a heartbeat.
RequestResponse ROUTER_BUSY 5 enum storeforward.proto Router is currently busy; please try again later.
RequestResponse ROUTER_HISTORY 6 enum storeforward.proto Router is responding to a request for history — carries history_messages (count), window and last_request.
RequestResponse ROUTER_STATS 7 enum storeforward.proto Router is responding to a request for stats — carries the Statistics variant.
RequestResponse ROUTER_TEXT_DIRECT 8 enum storeforward.proto Router replays a stored text message that was a direct message.
RequestResponse ROUTER_TEXT_BROADCAST 9 enum storeforward.proto Router replays a stored text message that was a broadcast.
RequestResponse CLIENT_ERROR 64 enum storeforward.proto Client is in an error state. Codes 064–127 are from the client; the server aborts an in-progress replay to that client.
RequestResponse CLIENT_HISTORY 65 enum storeforward.proto Client has requested a replay from the router — may carry a History variant whose window is in minutes.
RequestResponse CLIENT_STATS 66 enum storeforward.proto Client has requested stats from the router.
RequestResponse CLIENT_PING 67 enum storeforward.proto Client has requested the router respond; works as an "are you there" message.
RequestResponse CLIENT_PONG 68 enum storeforward.proto The response to a "Ping".
RequestResponse CLIENT_ABORT 106 enum storeforward.proto Client has requested that the router abort processing the client's request.
Message field rr 1 RequestResponse storeforward.proto The request/response code — every S&F packet carries one.
Message field stats (oneof variant) 2 Statistics storeforward.proto Server statistics: messages_total, messages_saved, messages_max, up_time (s), requests, requests_history, heartbeat, return_max, return_window (minutes).
Message field history (oneof variant) 3 History storeforward.proto History metadata: history_messages (count to be sent), window (the filter window used) and last_request (index of the last message previously sent, so a client can avoid duplicates).
Message field heartbeat (oneof variant) 4 Heartbeat storeforward.proto Heartbeat payload: period (seconds between heartbeats) and secondary ("If set, this is not the primary Store & Forward router on the mesh").
Message field text (oneof variant) 5 bytes storeforward.proto Text from a replayed history message (used with ROUTER_TEXT_DIRECT / ROUTER_TEXT_BROADCAST).

Demander l’historique en pratique

  • Depuis une app cliente : envoyez un protobuf StoreAndForward avec rr = CLIENT_HISTORY (65) sur STORE_FORWARD_APP. Incluez history.window en minutes pour outrepasser le défaut de 4 heures du serveur. Utilisez history.last_request de la réponse ROUTER_HISTORY précédente pour sauter les messages déjà reçus.
  • Déclencheur hérité : un message texte direct dont la charge utile commence par les octets ‘S’, ‘F’, 0x00 déclenche aussi une relecture de la fenêtre par défaut.
  • Vivacité : CLIENT_PINGROUTER_PONG confirme qu’un serveur est joignable; si le heartbeat du serveur est activé, il diffuse ROUTER_HEARTBEAT toutes les 900 s par défaut.
  • Santé : CLIENT_STATSROUTER_STATS retourne messages_saved vs messages_max, le temps de fonctionnement, les compteurs de requêtes et les return_max/return_window actifs du serveur.
  • Si le serveur est occupé à rejouer pour quelqu’un d’autre, vous recevez le message texte « busy » (ou ROUTER_BUSY pour les stats); le client patiente available_packets × 5000 ms, doublé après un ROUTER_ERROR.

Limites et mises en garde

  • Messages texte seulement. L’historique stocke les charges TEXT_MESSAGE_APP — pas de positions, de télémétrie ni d’autres ports.
  • ESP32 (ou build Linux natif) avec PSRAM seulement. Le corps du module ne compile que pour ARCH_ESP32 / ARCH_PORTDUINO, et le mode serveur exige ≥ 1 Mo de PSRAM libre — les nœuds nRF52 et les cartes sans PSRAM ne peuvent pas servir.
  • De la RAM, pas de la flash. Le tampon circulaire vit en PSRAM — un redémarrage du serveur perd l’historique stocké.
  • Un seul serveur par mesh est l’intention de conception. Le champ secondary du heartbeat existe, mais le micrologiciel commente « we always have one primary router for now ».
  • Pas sur le canal public. Les demandes d’historique sur le canal par défaut sont refusées; faites tourner le S&F sur un canal privé.
  • Travail en cours. portnums.proto marque le port « (Work in Progress) »; validez contre la version du micrologiciel que vous exécutez réellement.

Pourquoi ça compte pour des comms souveraines hors réseau

Un mesh dont les messages s’évaporent dès qu’un nœud s’éteint est un réseau de walkies-talkies; un mesh avec Store & Forward est un répondeur qui vous appartient. Pour la couche de communications résilientes d’une pile de souveraineté, la différence est structurelle : un nœud routeur (en anglais) solaire en hauteur avec le S&F activé signifie qu’un appareil éteint pendant une diffusion d’urgence peut rejoindre le mesh et demander « qu’est-ce que j’ai manqué? » — sans opérateur, sans nuage et sans compte. C’est le même principe d’auto-garde que nous appliquons à l’infrastructure Bitcoin : l’historique de vos communications devrait vivre sur du matériel que vous contrôlez. Voir aussi l’entrée de glossaire store-and-forward (en anglais) pour le concept réseau général, et les communications d’urgence par mesh au Canada (en anglais) pour le contexte de déploiement.

Pages reliées (en anglais)

Registre des PortNum Meshtastic (le port 65 vit ici) · Référence MeshPacket & champ Data (l’enveloppe des paquets S&F) · Rôles des nœuds Meshtastic (ROUTER / ROUTER_LATE) · Préréglages du modem LoRa · Meshtastic via MQTT (l’alternative reliée à internet) · Pôle mesh souverain (en français).

Crédit : Store & Forward est conçu et maintenu par le projet Meshtastic — le code du module dans meshtastic/firmware et le schéma du protocole dans meshtastic/protobufs (GPL-3.0). Cette page est un index daté, lisible par les humains et les machines, de ces fichiers.