Chapter 7 — Utilities

Three utility modules provide essential helper functions used throughout the library: openlcb_utilities.h (message manipulation), openlcb_float16.h (speed encoding), and openlcb_gridconnect.h (CAN-ASCII conversion).

7.1 Payload Insert Functions

All insert functions write data in big-endian byte order (network order, as required by the OpenLCB specification) and update payload_count automatically.

FunctionWritesOffset
OpenLcbUtilities_copy_event_id_to_openlcb_payload(msg, event_id) 8-byte event ID Always at offset 0
OpenLcbUtilities_copy_node_id_to_openlcb_payload(msg, node_id, offset) 6-byte node ID Caller-specified
OpenLcbUtilities_copy_byte_to_openlcb_payload(msg, byte, offset) 1 byte Caller-specified
OpenLcbUtilities_copy_word_to_openlcb_payload(msg, word, offset) 2-byte word (big-endian) Caller-specified
OpenLcbUtilities_copy_dword_to_openlcb_payload(msg, dword, offset) 4-byte doubleword (big-endian) Caller-specified
OpenLcbUtilities_copy_string_to_openlcb_payload(msg, str, offset) Null-terminated string Caller-specified
OpenLcbUtilities_copy_byte_array_to_openlcb_payload(msg, array, offset, count) Byte array Caller-specified

Example: Building a Verified Node ID Reply

// Allocate a BASIC buffer and populate it
openlcb_msg_t *reply = OpenLcbBufferStore_allocate_buffer(BASIC);
if (reply) {
    OpenLcbUtilities_load_openlcb_message(reply,
        node->alias, node->id,  // source
        0, 0,                   // dest (global)
        MTI_VERIFIED_NODE_ID);

    OpenLcbUtilities_copy_node_id_to_openlcb_payload(reply, node->id, 0);
    // reply->payload_count is now 6

    send_openlcb_msg(reply);  // Ownership transfers
}

7.2 Payload Extract Functions

Extract functions read data in big-endian order. They do not modify payload_count.

FunctionReadsReturns
OpenLcbUtilities_extract_event_id_from_openlcb_payload(msg) 8 bytes at offset 0 event_id_t
OpenLcbUtilities_extract_node_id_from_openlcb_payload(msg, offset) 6 bytes at offset node_id_t
OpenLcbUtilities_extract_byte_from_openlcb_payload(msg, offset) 1 byte at offset uint8_t
OpenLcbUtilities_extract_word_from_openlcb_payload(msg, offset) 2 bytes at offset uint16_t
OpenLcbUtilities_extract_dword_from_openlcb_payload(msg, offset) 4 bytes at offset uint32_t

7.3 Message Classification Helpers

FunctionPurpose
OpenLcbUtilities_is_addressed_openlcb_message(msg) Returns true if the MTI has the destination-address-present bit set (MASK_DEST_ADDRESS_PRESENT)
OpenLcbUtilities_is_addressed_message_for_node(node, msg) Returns true if the message destination matches this node's alias or Node ID
OpenLcbUtilities_count_nulls_in_openlcb_payload(msg) Counts null bytes (0x00) in the payload. Used for SNIP validation (SNIP has multiple null-terminated strings).
OpenLcbUtilities_set_multi_frame_flag(target, flag) Sets the multi-frame control bits (upper nibble) in the first data byte, preserving the lower nibble

7.4 Event Range Utilities

Event ranges allow a node to declare that it produces or consumes a contiguous block of event IDs with a single message, rather than listing each event individually.

event_id_t OpenLcbUtilities_generate_event_range_id(event_id_t base, event_range_count_enum count);
bool OpenLcbUtilities_is_event_id_in_consumer_ranges(openlcb_node_t *node, event_id_t event_id);
bool OpenLcbUtilities_is_event_id_in_producer_ranges(openlcb_node_t *node, event_id_t event_id);

7.5 Broadcast Time Event Encoding/Decoding

The Broadcast Time protocol encodes time, date, year, and rate information into 64-bit event IDs. The utilities module provides both encoding and decoding functions.

Decoding (Event ID to Values)

FunctionExtracts
OpenLcbUtilities_is_broadcast_time_event(event_id)Returns true if event ID is in the broadcast time space
OpenLcbUtilities_extract_clock_id_from_time_event(event_id)48-bit clock identifier (upper 6 bytes)
OpenLcbUtilities_get_broadcast_time_event_type(event_id)Event type enum (time, date, year, rate, query, etc.)
OpenLcbUtilities_extract_time_from_event_id(event_id, &hour, &minute)Hour (0-23) and minute (0-59)
OpenLcbUtilities_extract_date_from_event_id(event_id, &month, &day)Month (1-12) and day (1-31)
OpenLcbUtilities_extract_year_from_event_id(event_id, &year)Year (0-4095)
OpenLcbUtilities_extract_rate_from_event_id(event_id, &rate)12-bit signed fixed-point rate (10.2 format)

Encoding (Values to Event ID)

FunctionCreates
OpenLcbUtilities_create_time_event_id(clock_id, hour, minute, is_set)Report/Set Time event ID
OpenLcbUtilities_create_date_event_id(clock_id, month, day, is_set)Report/Set Date event ID
OpenLcbUtilities_create_year_event_id(clock_id, year, is_set)Report/Set Year event ID
OpenLcbUtilities_create_rate_event_id(clock_id, rate, is_set)Report/Set Rate event ID
OpenLcbUtilities_create_command_event_id(clock_id, command)Query, Start, Stop, or Date Rollover event ID

The is_set parameter distinguishes between Report events (sent by a clock producer to announce current state) and Set events (sent by a consumer to change the clock).

7.6 Train Search Event Encoding/Decoding

Train search uses event IDs in the 0x090099FF________ space to encode a DCC address and control flags:

bool OpenLcbUtilities_is_train_search_event(event_id_t event_id);
void OpenLcbUtilities_extract_train_search_digits(event_id_t event_id, uint8_t *digits);
uint8_t OpenLcbUtilities_extract_train_search_flags(event_id_t event_id);
uint16_t OpenLcbUtilities_train_search_digits_to_address(const uint8_t *digits);
event_id_t OpenLcbUtilities_create_train_search_event_id(uint16_t address, uint8_t flags);

The event ID encodes 6 BCD-like nibbles for the address (with 0xF meaning "unused digit") and a flags byte in the lowest byte containing:

FlagValueMeaning
TRAIN_SEARCH_FLAG_ALLOCATE0x80Allocate new train node if no match
TRAIN_SEARCH_FLAG_EXACT0x40Exact match only
TRAIN_SEARCH_FLAG_ADDRESS_ONLY0x20Match address, not name
TRAIN_SEARCH_FLAG_DCC0x08DCC protocol
TRAIN_SEARCH_FLAG_LONG_ADDR0x04Force long (14-bit) DCC address
TRAIN_SEARCH_SPEED_STEP_MASK0x03Speed step mode (0=default, 1=14, 2=28, 3=128)

7.7 Float16: IEEE 754 Half-Precision

The OpenLCB Train Protocol encodes speed as IEEE 754 binary16 (half-precision floating point). The sign bit doubles as the direction indicator: 0 = forward, 1 = reverse. The openlcb_float16.h module provides integer-only bit manipulation for embedded targets that lack hardware float16 support.

Constants

ConstantValueMeaning
FLOAT16_POSITIVE_ZERO0x0000Forward, stopped
FLOAT16_NEGATIVE_ZERO0x8000Reverse, stopped
FLOAT16_NAN0x7E00Speed not available
FLOAT16_SIGN_MASK0x8000Direction bit (bit 15)
FLOAT16_EXPONENT_MASK0x7C005-bit exponent (bits 14-10)
FLOAT16_MANTISSA_MASK0x03FF10-bit mantissa (bits 9-0)

Conversion Functions

uint16_t OpenLcbFloat16_from_float(float value);     // float32 to float16 bits
float    OpenLcbFloat16_to_float(uint16_t half);      // float16 bits to float32
uint16_t OpenLcbFloat16_negate(uint16_t half);        // Flip direction bit
bool     OpenLcbFloat16_is_nan(uint16_t half);        // Test for NaN
bool     OpenLcbFloat16_is_zero(uint16_t half);       // Test for +/- zero

Speed + Direction Helpers

uint16_t OpenLcbFloat16_speed_with_direction(float speed, bool reverse);
float    OpenLcbFloat16_get_speed(uint16_t half);     // Magnitude only (abs)
bool     OpenLcbFloat16_get_direction(uint16_t half); // true = reverse

Example: Setting Train Speed

// Set train to 50.0 mph forward
uint16_t f16 = OpenLcbFloat16_speed_with_direction(50.0f, false);
// f16 is now the float16 bit pattern for 50.0 forward

// Extract speed and direction
float speed = OpenLcbFloat16_get_speed(f16);    // 50.0
bool reverse = OpenLcbFloat16_get_direction(f16); // false

// Emergency stop: set to +0 (forward, stopped)
uint16_t stop = FLOAT16_POSITIVE_ZERO;  // 0x0000

7.8 GridConnect: ASCII CAN Format

GridConnect is an ASCII encoding of CAN frames used for serial and TCP/IP communication. The format is:

:X<8-hex-ID>N<hex-data>;

Examples:
:X19170AAAN050101011800;   (Verified Node ID from alias 0xAAA)
:X195B4AAAN0101000000010000;  (PCER event from alias 0xAAA)

The maximum string length is 29 bytes (MAX_GRID_CONNECT_LEN): 2 prefix (:X) + 8 ID + 1 (N) + 16 data (8 bytes x 2 hex chars) + 1 (;) + 1 NUL.

Streaming Parser

bool OpenLcbGridConnect_copy_out_gridconnect_when_done(uint8_t next_byte,
                                                        gridconnect_buffer_t *buffer);

Feeds one byte at a time from the incoming serial/TCP stream. Uses static internal state to track parsing progress through three states:

StateValueWaiting For
GRIDCONNECT_STATE_SYNC_START0:X or :x prefix
GRIDCONNECT_STATE_SYNC_FIND_HEADER28 hexadecimal identifier characters
GRIDCONNECT_STATE_SYNC_FIND_DATA4Data bytes until ; terminator

Returns true when a complete, valid message is ready in the buffer. Malformed input automatically resets the parser to the sync state.

stateDiagram-v2 [*] --> SYNC_START SYNC_START --> SYNC_FIND_HEADER : ':X' received SYNC_START --> SYNC_START : other bytes (ignored) SYNC_FIND_HEADER --> SYNC_FIND_DATA : 8 hex chars + 'N' SYNC_FIND_HEADER --> SYNC_START : invalid char (reset) SYNC_FIND_DATA --> SYNC_START : ';' received (message complete, return true) SYNC_FIND_DATA --> SYNC_START : overflow or invalid (reset)
Single Context Only

The streaming parser uses static state and is NOT thread-safe. It must only be called from a single context (e.g., the UART receive handler or the main loop, but not both).

Conversion Functions

void OpenLcbGridConnect_to_can_msg(gridconnect_buffer_t *buffer, can_msg_t *can_msg);
void OpenLcbGridConnect_from_can_msg(gridconnect_buffer_t *buffer, can_msg_t *can_msg);

7.9 Configuration Memory Buffer Operations

A parallel set of insert/extract functions operates on configuration_memory_buffer_t (64-byte) buffers used by the datagram protocol for configuration memory read/write operations:

node_id_t OpenLcbUtilities_extract_node_id_from_config_mem_buffer(buffer, index);
uint16_t  OpenLcbUtilities_extract_word_from_config_mem_buffer(buffer, index);
void      OpenLcbUtilities_copy_node_id_to_config_mem_buffer(buffer, node_id, index);
void      OpenLcbUtilities_copy_event_id_to_config_mem_buffer(buffer, event_id, index);
event_id_t OpenLcbUtilities_copy_config_mem_buffer_to_event_id(buffer, index);

These are used by the configuration memory protocol handler when reading and writing node configuration data through address spaces.

7.10 Emergency Event Detection

bool OpenLcbUtilities_is_emergency_event(event_id_t event_id);

Returns true if the event ID matches one of the four well-known emergency events: Emergency Off, Clear Emergency Off, Emergency Stop, or Clear Emergency Stop. Used by the train protocol to handle global emergency events.

← Previous: Ch 6b — Callback Interface Pattern Next: Ch 8 — Main Dispatcher →