Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions CMRI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ CMRI::CMRI(unsigned int address, unsigned int input_bits, unsigned int output_bi
// set state
, _rx_buffer((char *) malloc(_rx_length))
, _tx_buffer((char *) malloc(_tx_length))
, _rx_data_len(0)
, _init_handler(nullptr)

, _serial(serial_class)

Expand All @@ -55,6 +57,11 @@ void CMRI::set_address(unsigned int address)
_address = address;
}

void CMRI::set_init_handler(void (*handler)(const uint8_t *, int))
{
_init_handler = handler;
}

// reads in serial data, decodes packets
// automatically responds to POLL requests
// returns packet type so if we got a SET request you know to update your outputs
Expand All @@ -72,19 +79,23 @@ bool CMRI::process()

bool CMRI::process_char(char c)
{
// if it's a SET that's fine do nothing
// if it's an INIT that's also fine, we don't really care
// if it's a GET, well, do nothing since it must be someone else replying
// if it's a POLL then reply straight away with our data
switch (_decode(c))
// if it's a SET, update the output buffer
// if it's an INIT, the payload was consumed and the sketch can handle it
// if it's a GET, ignore — must be another node replying
// if it's a POLL, reply straight away with our input data
unsigned char ret = _decode(c);
switch (ret)
{
case POLL:
transmit();
return true;

case SET:
case INIT:
if (ret == INIT && _init_handler)
_init_handler((const uint8_t *)_rx_buffer, _rx_data_len);
return true;

default:
return false;
}
Expand Down Expand Up @@ -192,8 +203,11 @@ uint8_t CMRI::_decode(uint8_t c)
break;

case DECODE_CMD:
_rx_packet_type = c;
if (c == SET)
_mode = DECODE_DATA;
else if (c == INIT)
_mode = DECODE_DATA;
else if (c == POLL)
goto POSTAMBLE_POLL;
else
Expand Down Expand Up @@ -241,8 +255,10 @@ uint8_t CMRI::_decode(uint8_t c)
return NOOP;

POSTAMBLE_SET:
_rx_data_len = _rx_index;
_mode = PREAMBLE_1;
return SET;
_rx_index = 0;
return _rx_packet_type;

POSTAMBLE_POLL:
_mode = PREAMBLE_1;
Expand Down
3 changes: 3 additions & 0 deletions CMRI.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class CMRI
public:
CMRI(unsigned int address = 0, unsigned int input_bits = 24, unsigned int output_bits = 48, Stream& serial_class = Serial);
void set_address(unsigned int address);
void set_init_handler(void (*handler)(const uint8_t *data, int len));

bool process();
bool process_char(char c);
Expand Down Expand Up @@ -66,6 +67,8 @@ class CMRI
char _rx_packet_type;
char* _rx_buffer;
char* _tx_buffer;
int _rx_data_len;
void (*_init_handler)(const uint8_t *, int);

Stream& _serial;

Expand Down
72 changes: 72 additions & 0 deletions examples/init_handler/init_handler.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* C/MRI INIT handler example
* ===========================
* Demonstrates how to register an INIT callback to inspect the
* configuration payload sent by JMRI at startup.
*
* The INIT message body follows NMRA LCS-9.10.1:
* byte 0: NDP (Node Definition Parameter)
* bytes 1-2: DLH/DLL (transmit delay, high & low bytes)
* bytes 3+: node-type-specific options
*
* The transmit delay is computed as (DLH * 256 + DLL) * 10 microseconds.
* This example reads the delay and prints the configuration to Serial.
*
* To set up in JMRI:
* 1: Create a new C/MRI connection (Serial, 9600 baud)
* 2: Configure a node with address 0 — JMRI sends an INIT at startup
* 3: Open Tools > Tables > Lights and add a light at address 1
* 4: Open the C/MRI Monitor to watch the INIT message
* Raw format: [41 49 43 00 0A ...] = UA 'A', cmd 'I', NDP='C', DL=10
*/

#include <CMRI.h>

CMRI cmri; // defaults to a SMINI with address 0

// ---------------------------------------------------------------------------
// INIT handler callback
// Called automatically when an INIT ('I') packet is received.
// data – pointer to the raw INIT payload bytes
// len – number of bytes in the payload
// ---------------------------------------------------------------------------
void on_init(const uint8_t *data, int len) {
Serial.print(F("INIT received ("));

if (len >= 1) {
Serial.print(F("NDP="));
Serial.write(data[0]);
Serial.print(F(" "));
}

if (len >= 3) {
int delay_us = (data[1] * 256 + data[2]) * 10;
Serial.print(F("DL="));
Serial.print(delay_us);
Serial.print(F("us "));
}

if (len > 3) {
Serial.print(F("options="));
for (int i = 3; i < len; i++) {
if (i > 3) Serial.print(F(" "));
if (data[i] < 16) Serial.print(F("0"));
Serial.print(data[i], HEX);
}
Serial.print(F(" "));
}

Serial.print(len);
Serial.println(F(" bytes"));
}

void setup() {
Serial.begin(9600, SERIAL_8N2);
cmri.set_init_handler(on_init);
pinMode(13, OUTPUT);
}

void loop() {
cmri.process();
digitalWrite(13, cmri.get_bit(0));
}