diff --git a/IO_CMRI.cpp b/IO_CMRI.cpp index 9e3a9b3..992b00b 100644 --- a/IO_CMRI.cpp +++ b/IO_CMRI.cpp @@ -18,6 +18,7 @@ */ #include "IO_CMRI.h" +#include "defines.h" /************************************************************ * CMRIbus implementation @@ -36,12 +37,18 @@ CMRIbus::CMRIbus(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint } // Max message length is 256+6=262 bytes. - // Each byte is one start bit, 8 data bits and 1 stop bit = 10 bits per byte. - // Calculate timeout based on double this time. - _timeoutPeriod = 2 * 10 * 262 * 1000UL / (_baud / 1000UL); - - // Calculate the time in microseconds to transmit one byte (10 bits). - _byteTransmitTime = 1000000UL * 10 / _baud; + // Each byte is one start bit, 8 data bits and 1 or 2 stop bits, assume 11 bits per byte. + // Calculate timeout based on treble this time. + _timeoutPeriod = 3 * 11 * 262 * 1000UL / (_baud / 1000UL); +#if ARDUINOCMRI_COMPATIBLE + // NOTE: The ArduinoCMRI library, unless modified, contains a 'delay(50)' between + // receiving the end of the prompt message and starting to send the response. This + // is allowed for below. + _timeoutPeriod += 50000UL; +#endif + + // Calculate the time in microseconds to transmit one byte (11 bits max). + _byteTransmitTime = 1000000UL * 11 / _baud; // Postdelay is only required if we need to allow for data still being sent when // we want to switch off the transmitter. The flush() method of HardwareSerial // ensures that the data has completed being sent over the line. @@ -148,9 +155,9 @@ void CMRIbus::processOutgoing() { case TD_TRANSMIT: charsSent = sendData(_currentNode); _transmitState = TD_PROMPT; - // Defer next entry for as long as it takes to transmit the characters, - // to allow output queue to empty. - delayUntil(_currentMicros+_byteTransmitTime*charsSent); + // Defer next entry for as long as it takes to transmit the characters, + // to allow output queue to empty. Allow 2 bytes extra. + delayUntil(_currentMicros+_byteTransmitTime*(charsSent+2)); break; case TD_PROMPT: charsSent = requestData(_currentNode); @@ -184,7 +191,7 @@ void CMRIbus::processIncoming() { if (data == SYN) nextState = RD_SYN2; break; case RD_SYN2: - if (data == SYN) nextState = RD_STX; + if (data == SYN) nextState = RD_STX; else nextState = RD_SYN2; break; case RD_STX: if (data == STX) nextState = RD_ADDR; @@ -225,9 +232,17 @@ void CMRIbus::processIncoming() { void CMRIbus::enableTransmitter() { if (_transmitEnablePin != VPIN_NONE) ArduinoPins::fastWriteDigital(_transmitEnablePin, 1); - // Send an extra SYN character to ensure transmitter and - // remote receiver have stabilised before we start the packet. + // If we need a delay before we start the packet header, + // we can send a character or two to synchronise the + // transmitter and receiver. + // SYN characters should be used, but a bug in the + // ArduinoCMRI library causes it to ignore the packet if + // it's preceded by an odd number of SYN characters. + // So send a SYN followed by a NUL in that case. _serial->write(SYN); +#if ARDUINOCMRI_COMPATIBLE + _serial->write(NUL); // Reset the ArduinoCMRI library's parser +#endif } // If configured for half duplex RS485, switch RS485 interface diff --git a/IO_CMRI.h b/IO_CMRI.h index c8c922b..4f2d9b9 100644 --- a/IO_CMRI.h +++ b/IO_CMRI.h @@ -18,6 +18,8 @@ */ /* + * CMRIbus + * ======= * To define a CMRI bus, example syntax: * CMRIbus::create(bus, serial, baud[, cycletime[, pin]]); * @@ -29,6 +31,15 @@ * * Each bus must use a different serial port. * + * IMPORTANT: If you are using ArduinoCMRI library code by Michael Adams, at the time of writing this library + * is not compliant with the LCS-9.10.1 specification for CMRInet protocol. + * Various work-arounds may be enabled within the driver by adding the following line to your config.h file, + * to allow nodes running the ArduinoCMRI library to communicate: + * + * #define ARDUINOCMRI_COMPATIBLE TRUE + * + * CMRINode + * ======== * To define a CMRI node and associate it with a CMRI bus, * CMRInode::create(firstVPIN, numVPINs, bus, address, type [, inputs, outputs]); * @@ -196,6 +207,7 @@ private: // Definition of special characters in CMRInet protocol enum : uint8_t { + NUL = 0x00, STX = 0x02, ETX = 0x03, DLE = 0x10, @@ -209,8 +221,12 @@ public: // Device-specific initialisation void _begin() override { - // Some sources quote one stop bit, some two. + // CMRInet spec states one stop bit, JMRI and ArduinoCMRI use two stop bits +#if ARDUINOCMRI_COMPATIBLE + _serial->begin(_baud, SERIAL_8N2); +#else _serial->begin(_baud, SERIAL_8N1); +#endif #if defined(DIAG_IO) _display(); #endif