I've been working on BinktermPHP's PacketBBS support this weekend and have now added an AX.25 bridge alongside Meshcore for accessing netmail, echomail and bulletins from the BBS. I don't have a TNC or test packet setup handy so I whipped up an AX.25 HTTP KISS bridge.
The tool provides a console that simulates another radio user, while your kiss compatible ham radio app (or the binktermphp-ax25kiss bridge) connects to it on port 8001. There's also an HTTP API you can use for external tools.
Requires python3.
https://github.com/awehttam/kisstest
AX.25 HTTP KISS Bridge
This project emulates a simple KISS TNC for AX.25 testing.
The remote side connects in over TCP KISS on 127.0.0.1:8001 as though this process were a radio TNC. Locally, the interactive console behaves like a separate station sharing that TNC, so you can monitor incoming frames and send either connectionless UI traffic or connected-mode AX.25 frames.
An HTTP control API on 127.0.0.1:8101 is also exposed for local tooling and debugging.
Architecture
ax25_http_kiss_bridge.pyruns a TCP listener for the remote KISS client.- Incoming KISS frames are decoded, logged, and parsed as AX.25 when possible.
- The console acts as a local AX.25 endpoint using the configured
/mycall. - Plain text entered in the console is sent as:
UIwhen no connected AX.25 session is active- numbered
Iframes when a connected session has been established
- A small HTTP API exposes bridge state, received KISS frames, and event logs.
Run
python .\ax25_http_kiss_bridge.py --mycall N0CALL --kiss-host 127.0.0.1 --kiss-port 8001
Headless/service mode:
python .\ax25_http_kiss_bridge.py --mycall N0CALL --no-console
Console usage:
/mycall CALL[-SSID]
/target DEST [VIA ...]
/ui TEXT
/connect DEST [VIA ...]
/disconnect
/status
/raw HEX
/quit
Anything not starting with / is sent to the current remote station as a UI frame when disconnected, or as connected-mode I frame payload after a successful /connect.
Typical connectionless workflow:
/mycall N0CALL
/target VE7IGP
hello world
Typical connected-mode workflow:
/mycall N0CALL
/connect VE7IGP
hello once UA is received
HTTP interface
POST /tx- Body: raw KISS bytes containing one or more complete frames.
- Optional header:
X-KISS-Format: base64
GET /rx?timeout_ms=5000- Returns one queued received KISS frame as
application/octet-stream. - Returns
204 No Contentif no frame is available before timeout.
- Returns one queued received KISS frame as
GET /events?timeout_ms=5000- Returns one JSON event message for logging or UI display.
GET /status- Returns current bridge state as JSON.
Example transmit:
$frame = [byte[]](0xC0,0x00,0x82,0xA0,0xA4,0xA6,0x40,0x40,0x60,0x9C,0x60,0x86,0x82,0x98,0x98,0x61,0x03,0xF0,0x48,0x69,0xC0)
Invoke-WebRequest -Method Post -Uri http://127.0.0.1:8101/tx -Body $frame -ContentType application/octet-stream
Notes
- The radio-facing side is an inbound KISS TCP listener on
8001. - The HTTP API on
8101is a local side channel, not the radio-facing transport. - When the BBS/node sends AX.25 frames, they are decoded and logged, and payload text is printed to the console for
UIand connected-modeIframes. - The console can send connectionless
UIframes directly, and can also use AX.25 connected mode withSABM,UA,DISC,RR, andIframes when the peer supports it. - This is a pragmatic terminal bridge, not a full AX.25 stack with timers, retries, or digipeater routing logic.
Leave a Comment
No comments yet. Be the first to comment!