diff --git a/CMRI.cpp b/CMRI.cpp index e8c321d..4df57d7 100644 --- a/CMRI.cpp +++ b/CMRI.cpp @@ -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) @@ -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 @@ -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; } @@ -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 @@ -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; diff --git a/CMRI.h b/CMRI.h index 254e723..b9ebac6 100644 --- a/CMRI.h +++ b/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); @@ -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; diff --git a/examples/init_handler/init_handler.ino b/examples/init_handler/init_handler.ino new file mode 100644 index 0000000..37e53e1 --- /dev/null +++ b/examples/init_handler/init_handler.ino @@ -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 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)); +}