GridConnect
GridConnect carries CAN frames as text, :X19490ABCN;, over TCP (port 12021,
advertised over mDNS as _openlcb-can._tcp) and serial links. The
interfaces here read in a background thread, reconnect by themselves when the
link drops, and deliver pyolcb.frame.Frame objects to their listeners.
from pyolcb import Node, Address, GridConnectTcpInterface
interface = GridConnectTcpInterface("femtolcc-0001.local", 12021)
interface.wait_connected(5)
node = Node(Address("02.01.57.FF.00.01"), interface)
gridconnect
GridConnect, the ASCII encoding of CAN frames that OpenLCB uses over TCP
(port 12021 by convention, advertised over mDNS as _openlcb-can._tcp) and
over USB serial adapters:
:X19490ABCN; Verify Node ID, alias 0xABC, no data
:X19170ABCN020157000099; Verified Node ID 02.01.57.00.00.99
:X then the 29-bit identifier in hex, N then up to eight data bytes in
hex, and ;. Whitespace between frames is ignored.
GridConnectTcpInterface and GridConnectSerialInterface are
Interface implementations that read in a background
thread and reconnect by themselves when the link drops.
- class pyolcb.gridconnect.GridConnectInterface(reconnect: bool = True, retry_interval: float = 1.0, start: bool = True)
Base for GridConnect transports: a reader thread, listener fan-out and automatic reconnection. Subclasses implement
_open(),_read(),_write()and_close_link().- Parameters:
reconnect (bool) – Keep trying to (re)connect when the link fails. When
Falsethe interface stops after the first disconnect.retry_interval (float) – Seconds between reconnection attempts (doubling up to 30 s).
start (bool) – Start connecting immediately. Otherwise call
start().
- close()
Disconnect and stop reconnecting.
- property connected: bool
True while frames can be sent. A CAN bus is always connected.
- describe() str
A short human-readable description of the endpoint.
- register_listener(function: callable)
Register a function to be called with every frame received. On a CAN bus the argument is a
can.Message; on GridConnect interfaces it is aFrame. Both havearbitration_idanddata.
- send(message) bool
Send a
FrameorMessage. ReturnsFalse(and drops the frame) while disconnected.
- start()
Start the reader thread, which connects and keeps reconnecting.
- wait_connected(timeout: float = None) bool
Block until the link is up. Returns
Falseon timeout.
- class pyolcb.gridconnect.GridConnectParser
Incremental GridConnect decoder.
Feed it whatever arrives on the wire, in pieces of any size. Follows the same rules as AOLCB’s parser: a
:always starts a new frame (abandoning a partial one), text outside:…;is skipped, and malformed or overlong frames are dropped. That makes it safe on a serial console that mixes human-readable messages with frames.
- class pyolcb.gridconnect.GridConnectSerialInterface(port: str, baudrate: int = 115200, **kwargs)
GridConnect over a serial port: a USB-LCC adapter, or a FemtoLCC board’s USB console, which switches to bridging LCC traffic when it sees the first GridConnect frame from the computer. Requires pyserial.
RTS and DTR are held low so that opening the port does not reset an ESP32 through its USB-serial auto-reset circuit.
- Parameters:
port (str) – Device, e.g.
/dev/ttyACM0orCOM3.baudrate (int) – Line speed; USB CDC adapters ignore it.
- describe() str
A short human-readable description of the endpoint.
- class pyolcb.gridconnect.GridConnectTcpInterface(host: str, port: int = 12021, connect_timeout: float = 5.0, **kwargs)
GridConnect over TCP, as served by an OpenLCB hub, a FemtoLCC node’s WiFi link, JMRI’s hub or OpenMRN’s
hubprogram.- Parameters:
host (str) – Host name or address.
port (int) – TCP port, 12021 by default.
- describe() str
A short human-readable description of the endpoint.
- pyolcb.gridconnect.browse_mdns(timeout: float = 2.0) list[dict]
Find GridConnect TCP hubs advertised over mDNS as
_openlcb-can._tcp. Needs the optionalzeroconfpackage; returns[]without it. Each result is{"name", "host", "port", "addresses"}.
- pyolcb.gridconnect.format_frame(frame: Frame) str
Encode a frame as GridConnect text, without a line ending.
- pyolcb.gridconnect.list_serial_ports() list[dict]
Serial ports pyserial can see, as
{"device", "description"}dicts.
- pyolcb.gridconnect.mdns_available() bool
True if the optional
zeroconfpackage is installed.
- pyolcb.gridconnect.parse_frame(text: str) Frame | None
Decode one GridConnect frame such as
:X19490ABCN;. ReturnsNonefor anything that is not a valid extended data frame.
Frames
frame
Transport-neutral OpenLCB CAN frames.
Every OpenLCB frame is a 29-bit extended CAN frame, whether it travels on a real CAN bus or as GridConnect text over TCP or a serial line:
bit 28 always 1
bit 27 1 = OpenLCB message frame, 0 = CAN control frame
bits 26-12 variable field
bits 11-0 source alias
In a message frame bits 26-24 are the frame type. For type 1 (global or addressed message) bits 23-12 are the MTI; for types 2-5 (datagram) they are the destination alias. In a control frame bits 26-24 are 4-7 for the CID frames that carry a 12-bit slice of the node ID, and 0 for RID/AMD/AME/AMR.
Frame is duck-type compatible with can.Message (it has
arbitration_id, data and is_extended_id), so code written for
python-can frames works on GridConnect frames too.
- class pyolcb.frame.Frame(arbitration_id: int, data: bytes | bytearray | list[int] = b'')
A single OpenLCB CAN frame, independent of how it is carried.
- Parameters:
arbitration_id (int) – The 29-bit CAN identifier.
data (bytes | bytearray | list[int]) – Up to eight payload bytes.
- classmethod cid(sequence: int, node_id: int, alias: int) Frame
Check ID frame
sequence(7 = CID1 with node ID bits 47-36, down to 4 = CID4 with bits 11-0).
- classmethod control(variable: int, alias: int, data: bytes = b'') Frame
A CAN control frame (RID, AMD, AME, AMR, or a CID with the ID slice in
variable).
- property destination_alias: int | None
The destination alias of an addressed message or datagram frame, or
Nonefor a global message or control frame.
- property framing: int
The multi-frame flags (
ADDRESSED_*) of an addressed message.
- classmethod from_can_message(message) Frame
Convert a
can.Message(or anything witharbitration_idanddata).
- property is_addressed: bool
True for an addressed (not global) message frame.
- property is_cid: bool
True for the four Check ID frames of alias allocation.
- property is_control: bool
True for a CAN control frame (CID, RID, AMD, AME, AMR).
- classmethod message(mti: int, alias: int, data: bytes = b'') Frame
A global message frame, or one frame of an addressed message (
dataincludes the destination).
- property mti: int
The 12-bit MTI of a global or addressed message, 0 for anything else.
- property payload: bytes
The data bytes after the destination alias of an addressed message.
- pyolcb.frame.addressed_frames(mti: int, source: int, destination: int, payload: bytes = b'') list[Frame]
Split an addressed message into frames: two bytes of destination alias and framing flags, then up to six payload bytes per frame.
- pyolcb.frame.datagram_frames(source: int, destination: int, data: bytes) list[Frame]
Split a datagram (1-72 bytes) into only/first/middle/final frames.
- pyolcb.frame.format_event_id(event_id: int) str
0x0201570000990001->'02.01.57.00.00.99.00.01'.
- pyolcb.frame.format_node_id(node_id: int) str
0x020157000099->'02.01.57.00.00.99'.
- pyolcb.frame.parse_id(text: str | int | bytes, length: int) int
Parse a node ID (
length6) or event ID (length8) written as dotted hex (02.01.57.00.00.99), with other separators, or as plain hex.