diff --git a/examples/init_handler/init_handler.ino b/examples/init_handler/init_handler.ino new file mode 100644 index 0000000..e135649 --- /dev/null +++ b/examples/init_handler/init_handler.ino @@ -0,0 +1,81 @@ +/** + * 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 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)); +} diff --git a/src/CMRI.cpp b/src/CMRI.cpp index 6c91073..abe9fbe 100644 --- a/src/CMRI.cpp +++ b/src/CMRI.cpp @@ -39,7 +39,7 @@ CMRI::CMRI(unsigned int address, unsigned int input_bits, unsigned int output_bi // parsing state , - _mode(PREAMBLE_1), _rx_index(0) + _mode(PREAMBLE_1), _rx_index(0), _rx_data_len(0), _init_handler(nullptr) { // clear to zero @@ -54,6 +54,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 @@ -71,17 +76,21 @@ 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: @@ -188,8 +197,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 @@ -239,8 +251,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; diff --git a/src/CMRI.h b/src/CMRI.h index 8991889..052dbeb 100644 --- a/src/CMRI.h +++ b/src/CMRI.h @@ -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); @@ -82,6 +83,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;