feat: AX.25/AXUDP-Bridge — MeshCore-Channel ↔ AXUDP-Port (bidirektional) #20

Open
opened 2026-04-17 22:36:20 +02:00 by ppfeiffer · 0 comments
Owner

Idee

Ein Service-Plugin das einen konfigurierten MeshCore-Channel bidirektional mit einem AXUDP-Port verbindet. Damit können Nachrichten zwischen dem LoRa-Mesh und AX.25-Packet-Radio-Systemen (z.B. Direwolf, LinBPQ, BPQ32) ausgetauscht werden.

Anwendungsfälle

  • APRS-Gateway: MeshCore-Nachrichten als APRS-UI-Frames ins APRS-Netz spiegeln
  • Winlink-Bridge: Weiterleitung von MeshCore-Texten an ein Winlink-fähiges System
  • Packet-Radio-Kopplung: Verbindung von LoRa-Mesh mit klassischem 2m/70cm Packet

Technische Grundlagen

AXUDP-Protokoll

Roher AX.25-Frame als UDP-Payload, kein Header, kein Handshake. Typischer Port: 9300.

AX.25 UI-Frame-Struktur

Destination Callsign (7 Bytes, SSID im letzten Byte)
Source Callsign     (7 Bytes, SSID + End-of-Address-Bit)
Control              (0x03 = UI-Frame)
PID                  (0xF0 = kein Layer-3-Protokoll)
Info                 (Nutztext, max. 256 Bytes)

Callsign-Encoding: je 6 ASCII-Zeichen, links-ausgerichtet, Space-aufgefüllt, jedes Byte um 1 Bit links geshiftet.

Richtung MeshCore → AXUDP

Channel-Nachricht von Node "DN9EL" auf Channel "general"
  → AX.25 UI-Frame:
      FROM: DN9EL-0  (gemappt aus Node-Name, max. 6 Zeichen + SSID)
      TO:   MCMESH-0  (konfigurierbares Ziel-Callsign)
      INFO: "<channel> DN9EL: <text>"
  → UDP-Paket an <axudp_host>:<axudp_port>

Richtung AXUDP → MeshCore

UDP-Paket empfangen → AX.25-Frame parsen
  → Source-Callsign als Absender-ID
  → INFO-Feld als Nachrichtentext
  → Bot sendet Text an konfigurierten MeshCore-Channel

Einschränkungen

Einschränkung Details
Callsign-Länge AX.25 max. 6 Zeichen — MeshCore-Namen werden getrimmt/abgekürzt
Frame-Größe AX.25 UI max. 256 Bytes — längere MeshCore-Nachrichten werden abgeschnitten
Keine Bestätigung UI-Frames sind unacknowledged — kein Delivery-Feedback
Adressierung Ein festes Ziel-Callsign oder Callsign→Node-Mapping-Tabelle nötig

Implementierung als Service-Plugin

class AxudpBridgeService(BaseServicePlugin):
    # asyncio-Task: UDP-recv-Loop (eingehend AXUDP → MeshCore)
    # Bot-Event-Handler: Channel-Message-Hook (ausgehend MeshCore → AXUDP)
    # AX.25-Frame-Encoding: ~30 Zeilen Pure Python, keine externe Library

Konfiguration (config.ini / bot_config)

[AxudpBridge]
enabled = false
axudp_host = 127.0.0.1
axudp_port = 9300
axudp_listen_port = 9301
meshcore_channel = general
my_callsign = DL0MESH
destination_callsign = APRS
max_frame_size = 256
callsign_mapping = DN9EL:DN9EL0,DL1ABC:DL1ABC0

Offene Fragen

  • Soll die Bridge nur auf einen Channel horchen oder alle monitored_channels?
  • APRS-spezifisches PID/Format gewünscht (PID 0xF0 für Text, oder APRS PID 0x00)?
  • Bidirektionales Callsign-Mapping sinnvoll oder reicht Prefix-Abkürzung?
## Idee Ein Service-Plugin das einen konfigurierten MeshCore-Channel bidirektional mit einem AXUDP-Port verbindet. Damit können Nachrichten zwischen dem LoRa-Mesh und AX.25-Packet-Radio-Systemen (z.B. Direwolf, LinBPQ, BPQ32) ausgetauscht werden. ## Anwendungsfälle - **APRS-Gateway**: MeshCore-Nachrichten als APRS-UI-Frames ins APRS-Netz spiegeln - **Winlink-Bridge**: Weiterleitung von MeshCore-Texten an ein Winlink-fähiges System - **Packet-Radio-Kopplung**: Verbindung von LoRa-Mesh mit klassischem 2m/70cm Packet ## Technische Grundlagen ### AXUDP-Protokoll Roher AX.25-Frame als UDP-Payload, kein Header, kein Handshake. Typischer Port: 9300. ### AX.25 UI-Frame-Struktur ``` Destination Callsign (7 Bytes, SSID im letzten Byte) Source Callsign (7 Bytes, SSID + End-of-Address-Bit) Control (0x03 = UI-Frame) PID (0xF0 = kein Layer-3-Protokoll) Info (Nutztext, max. 256 Bytes) ``` Callsign-Encoding: je 6 ASCII-Zeichen, links-ausgerichtet, Space-aufgefüllt, jedes Byte um 1 Bit links geshiftet. ### Richtung MeshCore → AXUDP ``` Channel-Nachricht von Node "DN9EL" auf Channel "general" → AX.25 UI-Frame: FROM: DN9EL-0 (gemappt aus Node-Name, max. 6 Zeichen + SSID) TO: MCMESH-0 (konfigurierbares Ziel-Callsign) INFO: "<channel> DN9EL: <text>" → UDP-Paket an <axudp_host>:<axudp_port> ``` ### Richtung AXUDP → MeshCore ``` UDP-Paket empfangen → AX.25-Frame parsen → Source-Callsign als Absender-ID → INFO-Feld als Nachrichtentext → Bot sendet Text an konfigurierten MeshCore-Channel ``` ## Einschränkungen | Einschränkung | Details | |---|---| | Callsign-Länge | AX.25 max. 6 Zeichen — MeshCore-Namen werden getrimmt/abgekürzt | | Frame-Größe | AX.25 UI max. 256 Bytes — längere MeshCore-Nachrichten werden abgeschnitten | | Keine Bestätigung | UI-Frames sind unacknowledged — kein Delivery-Feedback | | Adressierung | Ein festes Ziel-Callsign oder Callsign→Node-Mapping-Tabelle nötig | ## Implementierung als Service-Plugin ```python class AxudpBridgeService(BaseServicePlugin): # asyncio-Task: UDP-recv-Loop (eingehend AXUDP → MeshCore) # Bot-Event-Handler: Channel-Message-Hook (ausgehend MeshCore → AXUDP) # AX.25-Frame-Encoding: ~30 Zeilen Pure Python, keine externe Library ``` ## Konfiguration (config.ini / bot_config) ```ini [AxudpBridge] enabled = false axudp_host = 127.0.0.1 axudp_port = 9300 axudp_listen_port = 9301 meshcore_channel = general my_callsign = DL0MESH destination_callsign = APRS max_frame_size = 256 callsign_mapping = DN9EL:DN9EL0,DL1ABC:DL1ABC0 ``` ## Offene Fragen - Soll die Bridge nur auf einen Channel horchen oder alle monitored_channels? - APRS-spezifisches PID/Format gewünscht (PID 0xF0 für Text, oder APRS PID 0x00)? - Bidirektionales Callsign-Mapping sinnvoll oder reicht Prefix-Abkürzung?
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ppfeiffer/meshcore-bot#20
No description provided.