Back to Articles

AX.25 HTTP KISS Bridge

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.py runs 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:
    • UI when no connected AX.25 session is active
    • numbered I frames 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 Content if no frame is available before timeout.
  • 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 8101 is 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 UI and connected-mode I frames.
  • The console can send connectionless UI frames directly, and can also use AX.25 connected mode with SABM, UA, DISC, RR, and I frames 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

0/5000

No comments yet. Be the first to comment!