Serial companion bridge between a MeshCore node and the binkterm-php packet BBS gateway API.
The bridge connects to a MeshCore device over USB serial, receives direct messages from radio users, sends each message as a command to the BBS API, and relays the plain-text BBS response back to the originating MeshCore node.
MeshCore requires a contact record for a remote node before messages from that node will pass through to the bridge. The device can be configured to auto-add users based on adverts; when another node advertises itself, the local MeshCore node adds the contact and will then accept messages from that remote user. Contacts can also be added manually to allow reception from specific remote nodes.
- Uses the MeshCore binary companion protocol over USB/UART.
- Forwards MeshCore direct messages to
POST /api/packetbbs/command. - Verifies BBS API connectivity with
GET /api/verifyduring startup. - Polls
GET /api/packetbbs/pendingfor queued outbound BBS messages. - Sends long BBS responses as numbered text chunks.
- Reconnects automatically if the serial port is disconnected.
- Supports manual and scheduled MeshCore adverts.
- Provides optional debug and packet trace logging.
- Runs on PHP 8.1 or newer.
- PHP 8.1 or newer.
- Composer.
- A MeshCore node connected over USB serial.
- A reachable binkterm-php instance with the packet BBS API enabled.
- An API key accepted by the BBS packet API.
Platform notes:
- On Linux and other Unix-like systems, the bridge configures the serial port with
stty. - On Windows, PHP's FFI extension must be enabled for non-blocking COM port I/O.
Install dependencies:
composer installCreate a local configuration file:
cp bridge.json.example bridge.jsonEdit bridge.json for your BBS URL, API key, and serial port.
Example:
{
"bbs_url": "https://yourbbs.example.com",
"api_key": "replace-with-your-bbs-api-key",
"serial_port": "/dev/ttyUSB0",
"baud_rate": 115200,
"interface": "meshcore",
"max_send_line_length": 200,
"inter_chunk_delay_ms": 500,
"min_command_interval_seconds": 2,
"post_open_delay_seconds": 0,
"handshake_timeout_seconds": 30,
"reconnect_delay_seconds": 5,
"poll_interval_seconds": 30,
"poll_node_ids": [],
"advert_zero_hop_interval_seconds": 0,
"advert_flood_interval_seconds": 0,
"http_timeout": 15
}Common keys:
| Key | Required | Description |
|---|---|---|
bbs_url |
Yes | Base URL for the binkterm-php site, without a trailing API path. |
api_key |
Yes | Shared bearer token accepted by the BBS packet API. |
serial_port |
Yes | Serial device path, such as /dev/ttyUSB0, /dev/ttyACM0, or COM3. |
baud_rate |
No | Serial speed. Defaults to 115200. |
interface |
No | Interface name sent to the BBS API. Defaults to meshcore. |
max_send_line_length |
No | Maximum response chunk size before splitting long BBS replies. Defaults to 200. |
inter_chunk_delay_ms |
No | Delay between multi-part response chunks. Defaults to 500. |
min_command_interval_seconds |
No | Per-node command rate limit. Defaults to 2. |
post_open_delay_seconds |
No | Delay after opening the serial port before handshaking. Useful for devices that reboot on open. |
handshake_timeout_seconds |
No | Time to wait for MeshCore device and self info during startup. Defaults to 30. |
reconnect_delay_seconds |
No | Delay before reopening the serial port after an error. Defaults to 5. |
poll_interval_seconds |
No | How often to poll the BBS for pending outbound messages. Defaults to 30. |
poll_node_ids |
No | Node IDs to poll for pending outbound messages even before those users contact this bridge process. Defaults to an empty list. |
advert_zero_hop_interval_seconds |
No | Automatic local advert interval. 0 disables scheduled zero-hop adverts. |
advert_flood_interval_seconds |
No | Automatic flooded advert interval. 0 disables scheduled flood adverts. |
http_timeout |
No | HTTP request timeout in seconds. Defaults to 15. |
Additional optional keys supported by the bridge:
| Key | Description |
|---|---|
message_probe_interval_seconds |
How often to proactively ask the radio for queued messages. Defaults to 2.0; set to 0 to disable. |
message_sync_timeout_seconds |
How long to wait for a sync response before allowing another sync request. Defaults to 1.0; set to 0 to disable the timeout. |
Start with the default bridge.json:
php bridge.phpOr through Composer:
composer startUse a custom config path:
php bridge.php /path/to/bridge.jsonEnable bridge-level debug logging:
php bridge.php --debugEnable decoded packet trace logging:
php bridge.php --traceRun as a background daemon (Linux/Unix only; requires the pcntl and posix PHP extensions):
php bridge.php --daemon --pid-file=/var/run/bridge.pid --log-file=/var/log/bridge.log--pid-file and --log-file are optional. Output is discarded when --log-file is omitted. The PID file is removed automatically when the bridge stops.
Runtime console commands are available only when the bridge can poll STDIN in non-blocking mode. They are disabled for Windows TTY sessions and when running as a daemon.
| Command | Description |
|---|---|
advert or a |
Send a zero-hop advert immediately. |
flood or af |
Send a flooded advert immediately. |
help or ? |
Show available console commands. |
quit or exit |
Stop the bridge. |
Startup:
- Open the serial port.
- Send MeshCore
DeviceQuery. - Wait for
DeviceInfo. - Send
AppStart. - Wait for
SelfInfoand register the bridge node ID with the BBS API client. - Send zero-hop and flooded MeshCore adverts.
- Call
GET /api/verifyand display the returned BBS information.
Inbound radio-to-BBS flow:
-
MeshCore reports
MsgWaiting, or the bridge probes for queued messages. -
The bridge sends
SyncNextMessageuntil MeshCore reportsNoMoreMessages. -
Each direct contact message is checked against the local command table (see below).
-
Unrecognised commands are forwarded to the BBS:
POST /api/packetbbs/command Authorization: Bearer <api_key> -
The plain-text API response is sent back as a MeshCore direct message.
The following commands are handled directly by the bridge without a BBS round-trip:
| Command | Alias | Response |
|---|---|---|
test |
t |
Path (relay count or direct) and SNR of the received message. |
ping |
— | Pong! with the same path and SNR information. |
Example responses:
Test OK | path: direct | SNR: +7.25 dB
Pong! | path: 2 hops | SNR: +4.50 dB
SNR is only available when the MeshCore node uses the V3 companion protocol; older firmware reports n/a.
Outbound BBS-to-radio flow:
-
The bridge remembers nodes that have recently contacted it and also uses any configured
poll_node_ids. -
Every
poll_interval_seconds, it calls:GET /api/packetbbs/pending?node_id=<node>&bridge_node_id=<bridge> Authorization: Bearer <api_key> -
Any returned messages are sent to that MeshCore node.
The BBS-facing node ID is the lowercase 12-character hex encoding of the 6-byte MeshCore public-key prefix from a received direct message.
Example:
8f12ab34cd56
The bridge also sends its own full public key as bridge_node_id after MeshCore returns SelfInfo.
Linux:
"serial_port": "/dev/ttyUSB0"Windows:
"serial_port": "COM3"If the serial device cannot be opened on Linux, confirm the user running the bridge has permission to access the port. This often means adding the user to the appropriate serial group, such as dialout, then logging out and back in.
Config file not found
Copy bridge.json.example to bridge.json or pass the config path as the first argument.
Missing required config key
Set bbs_url, api_key, and serial_port in the config file.
Cannot open serial device
Check the serial port name, cable, device power, and OS-level permissions.
PHP FFI extension is required
On Windows, enable ffi in php.ini.
ERROR: Could not reach BBS
Check bbs_url, api_key, TLS certificate validity, network connectivity, and the BBS packet API routes.
BBS verify failed
Check that <bbs_url>/api/verify is reachable from the bridge host and accepts the configured API key.
No replies on the radio:
- Run with
--debugto see commands and BBS responses. - Run with
--traceto see decoded MeshCore packets. - Lower
max_send_line_lengthif long messages are not transmitting reliably. - Increase
inter_chunk_delay_msif multi-part responses are sent too quickly.
bridge.php CLI entry point
bridge.json.example Example runtime configuration
src/BbsApiClient.php HTTP client for the packet BBS API
src/MeshCoreBridge.php Main bridge loop and message handling
src/Packet.php MeshCore companion packet encoder/decoder
src/SerialPort.php Cross-platform serial port wrapper
BSD-3-Clause. See LICENSE.md.