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;
FlagSet ByCleared ByPurpose
allocatedBuffer store allocateBuffer store free (ref_count reaches 0)Prevents double allocation
inprocessCAN RX (first frame of multi-frame)CAN RX (final frame received)Marks partially assembled messages in buffer list
invalidFIFO invalidation on AMRNever (message is freed)Prevents processing stale messages from departed nodes
loopbackSibling dispatchMain dispatcher after processingEnsures 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;

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;
ValuePayload SizeTypical Use
BASIC16 bytesMost OpenLCB messages (events, verify node ID, protocol support)
DATAGRAM72 bytesDatagram protocol messages (config memory read/write)
SNIP256 bytesSNIP replies, events with payload
STREAM512 bytesStream 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

ConstantValueDescription
MTI_INITIALIZATION_COMPLETE0x0100Node initialization complete (full protocol)
MTI_INITIALIZATION_COMPLETE_SIMPLE0x0101Node initialization complete (simple node)
MTI_VERIFY_NODE_ID_ADDRESSED0x0488Request specific node to identify
MTI_VERIFY_NODE_ID_GLOBAL0x0490Request all nodes to identify
MTI_VERIFIED_NODE_ID0x0170Node ID verification response
MTI_VERIFIED_NODE_ID_SIMPLE0x0171Node ID verification response (simple)
MTI_OPTIONAL_INTERACTION_REJECTED0x0068Node cannot process the message (OIR)
MTI_TERMINATE_DUE_TO_ERROR0x00A8Fatal error, node terminating
MTI_PROTOCOL_SUPPORT_INQUIRY0x0828Query supported protocols
MTI_PROTOCOL_SUPPORT_REPLY0x0668Supported protocols reply (6-byte bitfield)

3.5.2 Event Transport MTIs

ConstantValueDescription
MTI_CONSUMER_IDENTIFY0x08F4Identify consumers of an event
MTI_CONSUMER_RANGE_IDENTIFIED0x04A4Consumer identifies event range
MTI_CONSUMER_IDENTIFIED_UNKNOWN0x04C7Consumer state unknown
MTI_CONSUMER_IDENTIFIED_SET0x04C4Consumer event is SET
MTI_CONSUMER_IDENTIFIED_CLEAR0x04C5Consumer event is CLEAR
MTI_CONSUMER_IDENTIFIED_RESERVED0x04C6Consumer event in RESERVED state
MTI_PRODUCER_IDENTIFY0x0914Identify producers of an event
MTI_PRODUCER_RANGE_IDENTIFIED0x0524Producer identifies event range
MTI_PRODUCER_IDENTIFIED_UNKNOWN0x0547Producer state unknown
MTI_PRODUCER_IDENTIFIED_SET0x0544Producer event is SET
MTI_PRODUCER_IDENTIFIED_CLEAR0x0545Producer event is CLEAR
MTI_PRODUCER_IDENTIFIED_RESERVED0x0546Producer event in RESERVED state
MTI_EVENTS_IDENTIFY_DEST0x0968Request specific node to identify events
MTI_EVENTS_IDENTIFY0x0970Request all nodes to identify events
MTI_EVENT_LEARN0x0594Teaching/learning event configuration
MTI_PC_EVENT_REPORT0x05B4Producer/Consumer Event Report (PCER)
MTI_PC_EVENT_REPORT_WITH_PAYLOAD0x0F14PCER with additional payload data

3.5.3 SNIP, Train, Stream, and Datagram MTIs

ConstantValueDescription
MTI_SIMPLE_NODE_INFO_REQUEST0x0DE8Request SNIP data from node
MTI_SIMPLE_NODE_INFO_REPLY0x0A08SNIP data reply
MTI_TRAIN_PROTOCOL0x05EBTrain control command
MTI_TRAIN_REPLY0x01E9Train control reply
MTI_SIMPLE_TRAIN_INFO_REQUEST0x0DA8Request train node info
MTI_SIMPLE_TRAIN_INFO_REPLY0x09C8Train node info reply
MTI_DATAGRAM0x1C48Datagram message
MTI_DATAGRAM_OK_REPLY0x0A28Datagram received OK
MTI_DATAGRAM_REJECTED_REPLY0x0A48Datagram rejected (with error)
MTI_STREAM_INIT_REQUEST0x0CC8Stream connection request
MTI_STREAM_INIT_REPLY0x0868Stream connection reply
MTI_STREAM_SEND0x1F88Stream data
MTI_STREAM_PROCEED0x0888Stream flow control
MTI_STREAM_COMPLETE0x08A8Stream 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:

ConstantBit ValueProtocol
PSI_SIMPLE0x800000Simple Node Protocol
PSI_DATAGRAM0x400000Datagram Protocol
PSI_STREAM0x200000Stream Protocol
PSI_MEMORY_CONFIGURATION0x100000Memory Configuration Protocol
PSI_RESERVATION0x080000Reservation Protocol
PSI_EVENT_EXCHANGE0x040000Event Exchange Protocol
PSI_IDENTIFICATION0x020000Identification Protocol
PSI_TEACHING_LEARNING0x010000Teaching/Learning Protocol
PSI_REMOTE_BUTTON0x008000Remote Button Protocol
PSI_ABBREVIATED_DEFAULT_CDI0x004000Abbreviated Default CDI
PSI_DISPLAY0x002000Display Protocol
PSI_SIMPLE_NODE_INFORMATION0x001000SNIP
PSI_CONFIGURATION_DESCRIPTION_INFO0x000800CDI Protocol
PSI_TRAIN_CONTROL0x000400Train Control Protocol
PSI_FUNCTION_DESCRIPTION0x000200FDI Protocol
PSI_FUNCTION_CONFIGURATION0x000040Function Configuration Protocol
PSI_FIRMWARE_UPGRADE0x000020Firmware Upgrade Protocol
PSI_FIRMWARE_UPGRADE_ACTIVE0x000010Firmware Upgrade Active

3.7 Error Codes

Permanent Errors (0x1xxx)

ConstantValueDescription
ERROR_PERMANENT0x1000Generic permanent error
ERROR_PERMANENT_CONFIG_MEM_ADDRESS_SPACE_UNKNOWN0x1001Unknown address space
ERROR_PERMANENT_CONFIG_MEM_OUT_OF_BOUNDS_INVALID_ADDRESS0x1002Address out of bounds
ERROR_PERMANENT_CONFIG_MEM_ADDRESS_WRITE_TO_READ_ONLY0x1003Write to read-only memory
ERROR_PERMANENT_SOURCE_NOT_PERMITTED0x1020Source not permitted
ERROR_PERMANENT_NOT_IMPLEMENTED0x1040Not implemented
ERROR_PERMANENT_NOT_IMPLEMENTED_SUBCOMMAND_UNKNOWN0x1041Unknown subcommand
ERROR_PERMANENT_NOT_IMPLEMENTED_COMMAND_UNKNOWN0x1042Unknown command
ERROR_PERMANENT_NOT_IMPLEMENTED_UNKNOWN_MTI_OR_TRANPORT_PROTOCOL0x1043Unknown MTI or transport
ERROR_PERMANENT_INVALID_ARGUMENTS0x1080Invalid arguments

Temporary Errors (0x2xxx)

ConstantValueDescription
ERROR_TEMPORARY0x2000Generic temporary error
ERROR_TEMPORARY_BUFFER_UNAVAILABLE0x2020Buffer or resource unavailable
ERROR_TEMPORARY_NOT_EXPECTED_OUT_OF_ORDER0x2040Message out of sequence
ERROR_TEMPORARY_TRANSFER_ERROR0x2080Transfer error
ERROR_TEMPORARY_TIME_OUT0x2011Timeout waiting for response
ERROR_TEMPORARY_OUT_OF_ORDER_MIDDLE_END_WITH_NO_START0x2041Middle/end frame without start
ERROR_TEMPORARY_OUT_OF_ORDER_START_BEFORE_LAST_END0x2042Start before previous end

3.8 Run States

The run_state field in openlcb_node_state_t tracks the login state machine progression:

ConstantValueDescription
RUNSTATE_INIT0Boot initialization, sets Node ID as seed
RUNSTATE_GENERATE_SEED1Generate new 48-bit seed (after collision)
RUNSTATE_GENERATE_ALIAS2Generate 12-bit alias from seed via LFSR
RUNSTATE_LOAD_CHECK_ID_073Send CID frame 7 (first 12 bits of Node ID)
RUNSTATE_LOAD_CHECK_ID_064Send CID frame 6 (2nd 12 bits)
RUNSTATE_LOAD_CHECK_ID_055Send CID frame 5 (3rd 12 bits)
RUNSTATE_LOAD_CHECK_ID_046Send CID frame 4 (last 12 bits)
RUNSTATE_WAIT_200ms7Wait 200ms for collision detection
RUNSTATE_LOAD_RESERVE_ID8Send RID frame to claim alias
RUNSTATE_LOAD_ALIAS_MAP_DEFINITION9Send AMD frame, node becomes Permitted
RUNSTATE_LOAD_INITIALIZATION_COMPLETE10Send Init Complete, node becomes Initialized
RUNSTATE_LOAD_CONSUMER_EVENTS11Broadcast all consumer event IDs
RUNSTATE_LOAD_PRODUCER_EVENTS12Broadcast all producer event IDs
RUNSTATE_LOGIN_COMPLETE13Callback for application startup messages
RUNSTATE_RUN14Normal operation -- process messages from FIFO
stateDiagram-v2 [*] --> INIT INIT --> GENERATE_SEED GENERATE_SEED --> GENERATE_ALIAS GENERATE_ALIAS --> CID7 CID7 --> CID6 CID6 --> CID5 CID5 --> CID4 CID4 --> WAIT_200ms WAIT_200ms --> GENERATE_SEED : collision WAIT_200ms --> RID : no collision RID --> AMD AMD --> INIT_COMPLETE INIT_COMPLETE --> CONSUMER_EVENTS CONSUMER_EVENTS --> PRODUCER_EVENTS PRODUCER_EVENTS --> LOGIN_COMPLETE LOGIN_COMPLETE --> RUN state "RUNSTATE_INIT (0)" as INIT state "GENERATE_SEED (1)" as GENERATE_SEED state "GENERATE_ALIAS (2)" as GENERATE_ALIAS state "CID7 (3)" as CID7 state "CID6 (4)" as CID6 state "CID5 (5)" as CID5 state "CID4 (6)" as CID4 state "WAIT_200ms (7)" as WAIT_200ms state "RID (8)" as RID state "AMD (9)" as AMD state "INIT_COMPLETE (10)" as INIT_COMPLETE state "CONSUMER_EVENTS (11)" as CONSUMER_EVENTS state "PRODUCER_EVENTS (12)" as PRODUCER_EVENTS state "LOGIN_COMPLETE (13)" as LOGIN_COMPLETE state "RUN (14)" as RUN
← Previous: Ch 2 — Building and Running Next: Ch 3b — CAN-Specific Types →