Chapter 3 — Types and Constants (Transport-Independent)
This chapter covers the core type definitions in openlcb_types.h and the protocol constants in openlcb_defines.h. These files are the foundation of the entire library -- every other module includes them.
3.1 Fundamental Typedefs
| Typedef | Underlying Type | Description |
|---|---|---|
node_id_t |
uint64_t |
48-bit Node ID stored in a 64-bit type (upper 16 bits unused). NULL_NODE_ID = 0x000000000000. |
event_id_t |
uint64_t |
64-bit Event ID. NULL_EVENT_ID = 0x0000000000000000. |
3.2 The openlcb_msg_t Structure
The core message structure. Every message flowing through the library is represented by this type.
typedef struct {
openlcb_msg_state_t state; // Bitfield: allocated, inprocess, invalid, loopback
uint16_t mti; // Message Type Indicator
uint16_t source_alias; // 12-bit CAN alias of sender
uint16_t dest_alias; // 12-bit CAN alias of recipient (0 = global)
node_id_t source_id; // 48-bit Node ID of sender
node_id_t dest_id; // 48-bit Node ID of recipient (0 = global)
payload_type_enum payload_type; // BASIC, DATAGRAM, SNIP, or STREAM
uint16_t payload_count; // Valid bytes currently in payload
openlcb_payload_t *payload; // Pointer to payload buffer
openlcb_msg_timer_t timer; // Timer/retry union
uint8_t reference_count; // Number of active references
} openlcb_msg_t;
3.2.1 openlcb_msg_state_t Bitfield
Packed state flags that track the lifecycle of a message buffer:
typedef struct {
bool allocated : 1; // Buffer is in use (owned by buffer store)
bool inprocess : 1; // Multi-frame message being assembled
bool invalid : 1; // Invalidated (e.g., by AMR) -- discard on next access
bool loopback : 1; // Sibling dispatch copy -- skip source node, no re-loopback
} openlcb_msg_state_t;
| Flag | Set By | Cleared By | Purpose |
|---|---|---|---|
allocated | Buffer store allocate | Buffer store free (ref_count reaches 0) | Prevents double allocation |
inprocess | CAN RX (first frame of multi-frame) | CAN RX (final frame received) | Marks partially assembled messages in buffer list |
invalid | FIFO invalidation on AMR | Never (message is freed) | Prevents processing stale messages from departed nodes |
loopback | Sibling dispatch | Main dispatcher after processing | Ensures loopback copies skip the originating node |
3.2.2 openlcb_msg_timer_t Union
A single byte shared between two mutually exclusive uses depending on the message lifecycle stage:
typedef union {
uint8_t assembly_ticks; // Full 8-bit tick for multi-frame assembly timeout
struct {
uint8_t tick_snapshot : 5; // 5-bit tick snapshot (0-31) for datagram retry
uint8_t retry_count : 3; // Datagram retry counter (0-7)
} datagram;
} openlcb_msg_timer_t;
- assembly_ticks: Used during multi-frame message assembly. The CAN RX handler snapshots the current tick when the first frame arrives; the buffer list timeout scanner compares elapsed ticks to detect stalled assemblies.
- datagram.tick_snapshot + retry_count: Used during datagram retry logic. When a Datagram Rejected reply arrives with a temporary error, the tick snapshot and retry counter track when to resend and how many attempts remain.
3.2.3 payload_type_enum
typedef enum {
BASIC, // 16-byte payload buffer
DATAGRAM, // 72-byte payload buffer
SNIP, // 256-byte payload buffer
STREAM // 512-byte payload buffer
} payload_type_enum;
| Value | Payload Size | Typical Use |
|---|---|---|
BASIC | 16 bytes | Most OpenLCB messages (events, verify node ID, protocol support) |
DATAGRAM | 72 bytes | Datagram protocol messages (config memory read/write) |
SNIP | 256 bytes | SNIP replies, events with payload |
STREAM | 512 bytes | Stream data transfers |
3.3 Event Types
3.3.1 event_id_struct_t
An event ID paired with its current status, used in producer and consumer lists:
typedef struct {
event_id_t event; // 64-bit Event ID
event_status_enum status; // EVENT_STATUS_UNKNOWN, EVENT_STATUS_SET, EVENT_STATUS_CLEAR
} event_id_struct_t;
3.3.2 event_id_range_t
A contiguous range of event IDs for range-identified events:
typedef struct {
event_id_t start_base; // Starting Event ID
event_range_count_enum event_count; // Number of consecutive Event IDs
} event_id_range_t;
The event_range_count_enum provides power-of-two sizes from 1 up to 32768:
typedef enum {
EVENT_RANGE_COUNT_1 = 0,
EVENT_RANGE_COUNT_2 = 2,
EVENT_RANGE_COUNT_4 = 4,
// ... powers of 2 ...
EVENT_RANGE_COUNT_32768 = 32768
} event_range_count_enum;
3.4 Node Types
3.4.1 openlcb_node_t Structure
The complete representation of a virtual node:
typedef struct openlcb_node_TAG {
openlcb_node_state_t state; // Bitfield: run_state, allocated, permitted, etc.
uint64_t id; // 48-bit Node ID
uint16_t alias; // 12-bit CAN alias
uint64_t seed; // Seed for alias generation (LFSR)
event_id_consumer_list_t consumers; // Consumed event list + enumerator
event_id_producer_list_t producers; // Produced event list + enumerator
const node_parameters_t *parameters; // Pointer to const configuration
uint16_t timerticks; // 100ms timer tick counter
uint64_t owner_node; // Node ID that has locked this node
openlcb_msg_t *last_received_datagram; // Saved for reply processing
uint8_t index; // Index in node array
struct train_state_TAG *train_state; // NULL if not a train node
} openlcb_node_t;
3.4.2 openlcb_node_state_t Bitfield
typedef struct {
uint8_t run_state : 5; // Login state machine state (0-31)
bool allocated : 1; // Node is allocated
bool permitted : 1; // CAN alias allocated and claimed
bool initialized : 1; // Node fully initialized (Init Complete sent)
bool duplicate_id_detected : 1; // Duplicate Node ID conflict
bool openlcb_datagram_ack_sent : 1; // Datagram ACK sent, awaiting reply
bool resend_datagram : 1; // Resend last datagram (retry logic)
bool firmware_upgrade_active : 1; // Firmware upgrade in progress
} openlcb_node_state_t;
3.4.3 node_parameters_t Structure
Configuration parameters for a node. Typically stored in const (flash) memory:
typedef struct node_parameters_TAG {
user_snip_struct_t snip; // SNIP strings (manufacturer info)
uint64_t protocol_support; // Protocol Support Indicator bits
uint8_t consumer_count_autocreate; // Auto-create N consumer events from Node ID
uint8_t producer_count_autocreate; // Auto-create N producer events from Node ID
uint8_t cdi[USER_DEFINED_CDI_LENGTH]; // CDI XML data
uint8_t fdi[USER_DEFINED_FDI_LENGTH]; // FDI data (train nodes)
user_address_space_info_t address_space_configuration_definition; // Space 0xFF
user_address_space_info_t address_space_all; // Space 0xFE
user_address_space_info_t address_space_config_memory; // Space 0xFD
user_address_space_info_t address_space_acdi_manufacturer; // Space 0xFC
user_address_space_info_t address_space_acdi_user; // Space 0xFB
user_address_space_info_t address_space_train_function_definition_info; // Space 0xFA
user_address_space_info_t address_space_train_function_config_memory; // Space 0xF9
user_configuration_options configuration_options;
user_address_space_info_t address_space_firmware; // Space 0xEF
} node_parameters_t;
3.5 MTI Constants
All Message Type Indicator values are defined in openlcb_defines.h. The MTI is a 16-bit value carried in the mti field of openlcb_msg_t.
3.5.1 Message Network MTIs
| Constant | Value | Description |
|---|---|---|
MTI_INITIALIZATION_COMPLETE | 0x0100 | Node initialization complete (full protocol) |
MTI_INITIALIZATION_COMPLETE_SIMPLE | 0x0101 | Node initialization complete (simple node) |
MTI_VERIFY_NODE_ID_ADDRESSED | 0x0488 | Request specific node to identify |
MTI_VERIFY_NODE_ID_GLOBAL | 0x0490 | Request all nodes to identify |
MTI_VERIFIED_NODE_ID | 0x0170 | Node ID verification response |
MTI_VERIFIED_NODE_ID_SIMPLE | 0x0171 | Node ID verification response (simple) |
MTI_OPTIONAL_INTERACTION_REJECTED | 0x0068 | Node cannot process the message (OIR) |
MTI_TERMINATE_DUE_TO_ERROR | 0x00A8 | Fatal error, node terminating |
MTI_PROTOCOL_SUPPORT_INQUIRY | 0x0828 | Query supported protocols |
MTI_PROTOCOL_SUPPORT_REPLY | 0x0668 | Supported protocols reply (6-byte bitfield) |
3.5.2 Event Transport MTIs
| Constant | Value | Description |
|---|---|---|
MTI_CONSUMER_IDENTIFY | 0x08F4 | Identify consumers of an event |
MTI_CONSUMER_RANGE_IDENTIFIED | 0x04A4 | Consumer identifies event range |
MTI_CONSUMER_IDENTIFIED_UNKNOWN | 0x04C7 | Consumer state unknown |
MTI_CONSUMER_IDENTIFIED_SET | 0x04C4 | Consumer event is SET |
MTI_CONSUMER_IDENTIFIED_CLEAR | 0x04C5 | Consumer event is CLEAR |
MTI_CONSUMER_IDENTIFIED_RESERVED | 0x04C6 | Consumer event in RESERVED state |
MTI_PRODUCER_IDENTIFY | 0x0914 | Identify producers of an event |
MTI_PRODUCER_RANGE_IDENTIFIED | 0x0524 | Producer identifies event range |
MTI_PRODUCER_IDENTIFIED_UNKNOWN | 0x0547 | Producer state unknown |
MTI_PRODUCER_IDENTIFIED_SET | 0x0544 | Producer event is SET |
MTI_PRODUCER_IDENTIFIED_CLEAR | 0x0545 | Producer event is CLEAR |
MTI_PRODUCER_IDENTIFIED_RESERVED | 0x0546 | Producer event in RESERVED state |
MTI_EVENTS_IDENTIFY_DEST | 0x0968 | Request specific node to identify events |
MTI_EVENTS_IDENTIFY | 0x0970 | Request all nodes to identify events |
MTI_EVENT_LEARN | 0x0594 | Teaching/learning event configuration |
MTI_PC_EVENT_REPORT | 0x05B4 | Producer/Consumer Event Report (PCER) |
MTI_PC_EVENT_REPORT_WITH_PAYLOAD | 0x0F14 | PCER with additional payload data |
3.5.3 SNIP, Train, Stream, and Datagram MTIs
| Constant | Value | Description |
|---|---|---|
MTI_SIMPLE_NODE_INFO_REQUEST | 0x0DE8 | Request SNIP data from node |
MTI_SIMPLE_NODE_INFO_REPLY | 0x0A08 | SNIP data reply |
MTI_TRAIN_PROTOCOL | 0x05EB | Train control command |
MTI_TRAIN_REPLY | 0x01E9 | Train control reply |
MTI_SIMPLE_TRAIN_INFO_REQUEST | 0x0DA8 | Request train node info |
MTI_SIMPLE_TRAIN_INFO_REPLY | 0x09C8 | Train node info reply |
MTI_DATAGRAM | 0x1C48 | Datagram message |
MTI_DATAGRAM_OK_REPLY | 0x0A28 | Datagram received OK |
MTI_DATAGRAM_REJECTED_REPLY | 0x0A48 | Datagram rejected (with error) |
MTI_STREAM_INIT_REQUEST | 0x0CC8 | Stream connection request |
MTI_STREAM_INIT_REPLY | 0x0868 | Stream connection reply |
MTI_STREAM_SEND | 0x1F88 | Stream data |
MTI_STREAM_PROCEED | 0x0888 | Stream flow control |
MTI_STREAM_COMPLETE | 0x08A8 | Stream completed |
3.6 Protocol Support Indicator Bits
A 48-bit bitfield returned in the Protocol Support Reply message. Each bit indicates support for a protocol:
| Constant | Bit Value | Protocol |
|---|---|---|
PSI_SIMPLE | 0x800000 | Simple Node Protocol |
PSI_DATAGRAM | 0x400000 | Datagram Protocol |
PSI_STREAM | 0x200000 | Stream Protocol |
PSI_MEMORY_CONFIGURATION | 0x100000 | Memory Configuration Protocol |
PSI_RESERVATION | 0x080000 | Reservation Protocol |
PSI_EVENT_EXCHANGE | 0x040000 | Event Exchange Protocol |
PSI_IDENTIFICATION | 0x020000 | Identification Protocol |
PSI_TEACHING_LEARNING | 0x010000 | Teaching/Learning Protocol |
PSI_REMOTE_BUTTON | 0x008000 | Remote Button Protocol |
PSI_ABBREVIATED_DEFAULT_CDI | 0x004000 | Abbreviated Default CDI |
PSI_DISPLAY | 0x002000 | Display Protocol |
PSI_SIMPLE_NODE_INFORMATION | 0x001000 | SNIP |
PSI_CONFIGURATION_DESCRIPTION_INFO | 0x000800 | CDI Protocol |
PSI_TRAIN_CONTROL | 0x000400 | Train Control Protocol |
PSI_FUNCTION_DESCRIPTION | 0x000200 | FDI Protocol |
PSI_FUNCTION_CONFIGURATION | 0x000040 | Function Configuration Protocol |
PSI_FIRMWARE_UPGRADE | 0x000020 | Firmware Upgrade Protocol |
PSI_FIRMWARE_UPGRADE_ACTIVE | 0x000010 | Firmware Upgrade Active |
3.7 Error Codes
Permanent Errors (0x1xxx)
| Constant | Value | Description |
|---|---|---|
ERROR_PERMANENT | 0x1000 | Generic permanent error |
ERROR_PERMANENT_CONFIG_MEM_ADDRESS_SPACE_UNKNOWN | 0x1001 | Unknown address space |
ERROR_PERMANENT_CONFIG_MEM_OUT_OF_BOUNDS_INVALID_ADDRESS | 0x1002 | Address out of bounds |
ERROR_PERMANENT_CONFIG_MEM_ADDRESS_WRITE_TO_READ_ONLY | 0x1003 | Write to read-only memory |
ERROR_PERMANENT_SOURCE_NOT_PERMITTED | 0x1020 | Source not permitted |
ERROR_PERMANENT_NOT_IMPLEMENTED | 0x1040 | Not implemented |
ERROR_PERMANENT_NOT_IMPLEMENTED_SUBCOMMAND_UNKNOWN | 0x1041 | Unknown subcommand |
ERROR_PERMANENT_NOT_IMPLEMENTED_COMMAND_UNKNOWN | 0x1042 | Unknown command |
ERROR_PERMANENT_NOT_IMPLEMENTED_UNKNOWN_MTI_OR_TRANPORT_PROTOCOL | 0x1043 | Unknown MTI or transport |
ERROR_PERMANENT_INVALID_ARGUMENTS | 0x1080 | Invalid arguments |
Temporary Errors (0x2xxx)
| Constant | Value | Description |
|---|---|---|
ERROR_TEMPORARY | 0x2000 | Generic temporary error |
ERROR_TEMPORARY_BUFFER_UNAVAILABLE | 0x2020 | Buffer or resource unavailable |
ERROR_TEMPORARY_NOT_EXPECTED_OUT_OF_ORDER | 0x2040 | Message out of sequence |
ERROR_TEMPORARY_TRANSFER_ERROR | 0x2080 | Transfer error |
ERROR_TEMPORARY_TIME_OUT | 0x2011 | Timeout waiting for response |
ERROR_TEMPORARY_OUT_OF_ORDER_MIDDLE_END_WITH_NO_START | 0x2041 | Middle/end frame without start |
ERROR_TEMPORARY_OUT_OF_ORDER_START_BEFORE_LAST_END | 0x2042 | Start before previous end |
3.8 Run States
The run_state field in openlcb_node_state_t tracks the login state machine progression:
| Constant | Value | Description |
|---|---|---|
RUNSTATE_INIT | 0 | Boot initialization, sets Node ID as seed |
RUNSTATE_GENERATE_SEED | 1 | Generate new 48-bit seed (after collision) |
RUNSTATE_GENERATE_ALIAS | 2 | Generate 12-bit alias from seed via LFSR |
RUNSTATE_LOAD_CHECK_ID_07 | 3 | Send CID frame 7 (first 12 bits of Node ID) |
RUNSTATE_LOAD_CHECK_ID_06 | 4 | Send CID frame 6 (2nd 12 bits) |
RUNSTATE_LOAD_CHECK_ID_05 | 5 | Send CID frame 5 (3rd 12 bits) |
RUNSTATE_LOAD_CHECK_ID_04 | 6 | Send CID frame 4 (last 12 bits) |
RUNSTATE_WAIT_200ms | 7 | Wait 200ms for collision detection |
RUNSTATE_LOAD_RESERVE_ID | 8 | Send RID frame to claim alias |
RUNSTATE_LOAD_ALIAS_MAP_DEFINITION | 9 | Send AMD frame, node becomes Permitted |
RUNSTATE_LOAD_INITIALIZATION_COMPLETE | 10 | Send Init Complete, node becomes Initialized |
RUNSTATE_LOAD_CONSUMER_EVENTS | 11 | Broadcast all consumer event IDs |
RUNSTATE_LOAD_PRODUCER_EVENTS | 12 | Broadcast all producer event IDs |
RUNSTATE_LOGIN_COMPLETE | 13 | Callback for application startup messages |
RUNSTATE_RUN | 14 | Normal operation -- process messages from FIFO |