From 53d23770f6618cda0d4a89f0cd143da2e67a6eed Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Fri, 13 Dec 2024 04:04:13 -0500 Subject: [PATCH] tidy up and debug --- IO_RS485.cpp | 369 +++++++++++++++++++++------------------------------ IO_RS485.h | 213 ++++++++++++----------------- 2 files changed, 237 insertions(+), 345 deletions(-) diff --git a/IO_RS485.cpp b/IO_RS485.cpp index ade5013..f3cbfc9 100644 --- a/IO_RS485.cpp +++ b/IO_RS485.cpp @@ -21,111 +21,42 @@ #include "IO_RS485.h" #include "defines.h" -void RS485::setTransactionId(uint16_t transactionId) { - _setRegister(tcp, 0, transactionId); +/************************************************************ + * RS485 implementation + ************************************************************/ + +// Constructor for RS485 +RS485::RS485(HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA) { + _baud = baud; + _serialD = &serial; + _txPin = txPin; + _busNo = 0; + + _cycleTime = cycleTimeMS * 1000UL; // convert from milliseconds to microseconds. + _waitA = waitA; + if (_waitA < 3) _waitA = 3; + // Add device to HAL device chain + IODevice::addDevice(this); + + // Add bus to RS485 chain. + _nextBus = _busList; + _busList = this; } -void RS485::setProtocolId(uint16_t protocolId) { - _setRegister(tcp, 2, protocolId); -} - -void RS485::setLength(uint16_t length) { - if (length < 3 || length > 254) _setRegister(tcp, 4, 0); - else _setRegister(tcp, 4, length); -} - -void RS485::setUnitId(uint8_t unitId) { - tcp[6] = unitId; -} - -void RS485::setFunctionCode(uint8_t functionCode) { - pdu[0] = functionCode; -} - -void RS485::setDataRegister(uint8_t index, uint16_t value) { - _setRegister(data, index, value); -} - - - -void RS485::setRtuLen(uint16_t rtuLen) { - setLength(rtuLen - 2); -} - -void RS485::setTcpLen(uint16_t tcpLen) { - setLength(tcpLen - 6); -} - -void RS485::setPduLen(uint16_t pduLen) { - setLength(pduLen + 1); -} - -void RS485::setDataLen(uint16_t dataLen) { - setLength(dataLen + 2); -} - - - -uint16_t RS485::getTransactionId() { - return _getRegister(tcp, 0); -} - -uint16_t RS485::getProtocolId() { - return _getRegister(tcp, 2); -} - -uint16_t RS485::getLength() { - uint16_t length = _getRegister(tcp, 4); - if (length < 3 || length > 254) return 0; - else return length; -} - -uint8_t RS485::getUnitId() { - return tcp[6]; -} - -uint8_t RS485::getFunctionCode() { - return pdu[0]; -} - -uint16_t RS485::getDataRegister(uint8_t index) { - return _getRegister(data, index); -} - - - -uint16_t RS485::getRtuLen() { - uint16_t len = getLength(); - if (len == 0) return 0; - else return len + 2; -} - -uint16_t RS485::getTcpLen() { - uint16_t len = getLength(); - if (len == 0) return 0; - else return len + 6; -} - -uint16_t RS485::getPduLen() { - uint16_t len = getLength(); - if (len == 0) return 0; - else return len - 1; -} - -uint16_t RS485::getDataLen() { - uint16_t len = getLength(); - if (len == 0) return 0; - else return len - 2; -} - - - +/* -= updateCrc =- +// +// add the CRC value from _calculateCrc (2 bytes) to the buffer. +*/ void RS485::updateCrc(uint8_t *buf, uint16_t len) { uint16_t crc = _calculateCrc(buf, len); buf[len] = lowByte(crc); buf[len + 1] = highByte(crc); } +/* -= crcGood =- +// +// return TRUE if CRC matched between buffer copy, and calculated. +*/ bool RS485::crcGood(uint8_t *buf, uint16_t len) { uint16_t aduCrc = buf[len] | (buf[len + 1] << 8); uint16_t calculatedCrc = _calculateCrc(buf, len); @@ -133,15 +64,10 @@ bool RS485::crcGood(uint8_t *buf, uint16_t len) { else return false; } -void RS485::_setRegister(uint8_t *buf, uint16_t index, uint16_t value) { - buf[index] = highByte(value); - buf[index + 1] = lowByte(value); -} - -uint16_t RS485::_getRegister(uint8_t *buf, uint16_t index) { - return (buf[index] << 8) | buf[index + 1]; -} - +/* -= calculateCrc =- +// +// use bitwise XOR to calculate CRC into a 16-bit byte +*/ uint16_t RS485::_calculateCrc(uint8_t *buf, uint16_t len) { uint16_t value = 0xFFFF; for (uint16_t i = 0; i < len; i++) { @@ -155,12 +81,10 @@ uint16_t RS485::_calculateCrc(uint8_t *buf, uint16_t len) { return value; } - - -uint16_t div8RndUp(uint16_t value) { - return (value + 7) >> 3; -} - +/* -= clearRxBuffer =- +// +// BLOCKING method to empty stray data in RX buffer +*/ void RS485::clearRxBuffer() { unsigned long startMicros = micros(); do { @@ -171,34 +95,12 @@ void RS485::clearRxBuffer() { } while (micros() - startMicros < _frameTimeout); } - -/************************************************************ - * RS485 implementation - ************************************************************/ - - -// Constructor for RS485 -RS485::RS485(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA, int waitB) { - _baud = baud; - _serialD = &serial; - _txPin = txPin; - _busNo = busNo; - _cycleTime = cycleTimeMS * 1000UL; // convert from milliseconds to microseconds. - _waitA = waitA; - _waitB = waitB; - if (_waitA < 3) _waitA = 3; - if (_waitB < 2) _waitB = 2; - // Add device to HAL device chain - IODevice::addDevice(this); - - // Add bus to RS485 chain. - _nextBus = _busList; - _busList = this; -} - +/* -= _loop =- +// // Main loop function for RS485. // Work through list of nodes. For each node, in separate loop entries // When the slot time has finished, move on to the next device. +*/ void RS485::_loop(unsigned long currentMicros) { _currentMicros = currentMicros; @@ -218,8 +120,7 @@ void RS485::_loop(unsigned long currentMicros) { if (taskCnt > 0) { // run through tasks - int* taskData[25]; - getNextTask(taskData); + if (!waitReceive) getNextTask(taskData); switch((int) taskData[0]) { case 0: // protection for pulling empty task @@ -229,10 +130,10 @@ if (taskCnt > 0) { uint8_t pullup = (uint8_t) taskData[6]; uint8_t outBuffer[6] = {EXIODPUP, (uint8_t) taskData[0], (uint8_t)taskData[3], pullup}; uint8_t responseBuffer[3]; - updateCrc(outBuffer,sizeof(outBuffer)-2); + updateCrc(outBuffer,4); if (waitReceive == false) { if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); - _serialD->write(outBuffer, sizeof(outBuffer)); + _serialD->write(outBuffer, 6); _serialD->flush(); if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, LOW); } @@ -255,9 +156,12 @@ if (taskCnt > 0) { if (crcGood(responseBuffer,sizeof(responseBuffer)-2)) { if (responseBuffer[0] == EXIORDY) { } else { - DIAG(F("EXIOMB Vpin %u cannot be used as a digital input pin"), (int)taskData[3]); + DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), (int)taskData[3]); } - } else DIAG(F("EXIOMB node %d CRC Error"), (int) taskData[0]); + } else { + DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) taskData[0]); + flagOK = false; + } } else if (taskData[3] == (int*) CONFIGURE_ANALOGINPUT) { // TODO: Consider moving code from _configureAnalogIn() to here and remove _configureAnalogIn // from IODevice class definition. Not urgent, but each virtual function defined @@ -268,10 +172,10 @@ if (taskCnt > 0) { case 2: // configure analog in uint8_t commandBuffer[5] = {EXIOENAN, (uint8_t) taskData[0], (uint8_t) taskData[3]}; uint8_t responseBuffer[3]; - updateCrc(commandBuffer,sizeof(commandBuffer)-2); + updateCrc(commandBuffer,3); if (waitReceive == false) { if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); - _serialD->write(commandBuffer, sizeof(commandBuffer)); + _serialD->write(commandBuffer, 5); _serialD->flush(); if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, LOW); } @@ -293,9 +197,12 @@ if (taskCnt > 0) { if (crcGood(responseBuffer,sizeof(responseBuffer)-2)) { if (responseBuffer[0] != EXIORDY) { - DIAG(F("EX-IOExpanderMB: Vpin %u on node %d cannot be used as an analogue input pin"), (int) taskData[3], (int) taskData[0]); + DIAG(F("EX-IOExpander485: Vpin %u on node %d cannot be used as an analogue input pin"), (int) taskData[3], (int) taskData[0]); + } + } else { + DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) taskData[0]); + flagOK = false; } - } else DIAG(F("EXIOMB node %d CRC Error"), (int) taskData[0]); break; case 3: // write pin uint8_t digitalOutBuffer[6]; @@ -304,10 +211,10 @@ if (taskCnt > 0) { digitalOutBuffer[1] = (uint8_t) taskData[0]; digitalOutBuffer[2] = (uint8_t) taskData[3]; digitalOutBuffer[3] = (uint8_t) taskData[4]; - updateCrc(digitalOutBuffer,sizeof(digitalOutBuffer)-2); + updateCrc(digitalOutBuffer,4); if (waitReceive == false) { if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); - _serialD->write(digitalOutBuffer, sizeof(digitalOutBuffer)); + _serialD->write(digitalOutBuffer, 6); _serialD->flush(); if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, LOW); } @@ -328,15 +235,18 @@ if (taskCnt > 0) { } while (micros() - startMicros <= 500 && len < 256); if (crcGood(responseBuffer,sizeof(responseBuffer)-2)) { if (responseBuffer[0] != EXIORDY) { - DIAG(F("Vpin %u cannot be used as a digital output pin"), (int)taskData[3]); + DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital output pin"), (int)taskData[3]); + } + } else { + DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) taskData[0]); + flagOK = false; } - } else DIAG(F("EXIOMB node %d CRC Error"), (int) taskData[0]); break; case 4: uint8_t servoBuffer[10]; uint8_t responseBuffer[3]; #ifdef DIAG_IO - DIAG(F("Servo: WriteAnalogue Vpin:%u Value:%d Profile:%d Duration:%d %S"), + DIAG(F("EX-IOExpander485 Servo: WriteAnalogue Vpin:%u Value:%d Profile:%d Duration:%d %S"), vpin, value, profile, duration, _deviceState == DEVSTATE_FAILED?F("DEVSTATE_FAILED"):F("")); #endif servoBuffer[0] = EXIOWRAN; @@ -347,10 +257,10 @@ if (taskCnt > 0) { servoBuffer[5] = (uint8_t) taskData[5]; servoBuffer[6] = (uint8_t) taskData[6] & 0xFF; servoBuffer[7] = (uint8_t) taskData[6] >> 8; - updateCrc(servoBuffer,sizeof(servoBuffer)-2); + updateCrc(servoBuffer,8); if (waitReceive == false) { if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); - _serialD->write(servoBuffer, sizeof(servoBuffer)); + _serialD->write(servoBuffer, 10); _serialD->flush(); if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, LOW); } @@ -370,77 +280,93 @@ if (taskCnt > 0) { } } while (micros() - startMicros <= 500 && len < 256); if (!crcGood(responseBuffer,sizeof(responseBuffer)-2)) { - DIAG(F("EXIOMB node %d CRC Error"), (int) taskData[0]); + DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) taskData[0]); + flagOK = false; _deviceState = DEVSTATE_FAILED; } else { if (responseBuffer[0] != EXIORDY) { - DIAG(F("Vpin %u cannot be used as a servo/PWM pin"), (int) taskData[3]); + DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a servo/PWM pin"), (int) taskData[3]); } } } } else { memcpy(_currentNode->_analogueInputStates, _currentNode->_analogueInputBuffer, _currentNode->_analoguePinBytes); // Copy I2C input buffer to states - - if (_currentNode->_numDigitalPins>0 && currentMicros - _lastDigitalRead > _digitalRefresh) { // Delay for digital read refresh - // Issue new read request for digital states. As the request is non-blocking, the buffer has to - // be allocated from heap (object state). - _currentNode->_readCommandBuffer[0] = EXIORDD; - updateCrc(_currentNode->_readCommandBuffer,sizeof(_currentNode->_readCommandBuffer)-2); - if (waitReceive == false) { - if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); - _serialD->write(_currentNode->_readCommandBuffer, sizeof(_currentNode->_readCommandBuffer)); - _serialD->flush(); - if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, LOW); - } - unsigned long startMillis = millis(); - if (!_serialD->available()) { - if (waitReceive == true && _waitCounter > _waitA) { - flagOK = false; - } else waitReceive = true; - } - uint16_t len = 0; - unsigned long startMicros = micros(); - do { - if (_serialD->available()) { - startMicros = micros(); - _currentNode->_digitalInputStates[len] = _serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < (_currentNode->_numDigitalPins+7)/8); - if (!crcGood(_currentNode->_digitalInputStates,sizeof(_currentNode->_digitalInputStates)-2)) DIAG(F("MB CRC error on node %d"), _currentNode->getNodeID()); - _lastDigitalRead = currentMicros; - _readState = RDS_DIGITAL; -} else if (_currentNode->_numAnaloguePins>0 && currentMicros - _lastAnalogueRead > _analogueRefresh) { // Delay for analogue read refresh - // Issue new read for analogue input states - _currentNode->_readCommandBuffer[0] = EXIORDAN; - updateCrc(_currentNode->_readCommandBuffer,sizeof(_currentNode->_readCommandBuffer)-2); - if (waitReceive == false) { - if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); - _serialD->write(_currentNode->_readCommandBuffer, sizeof(_currentNode->_readCommandBuffer)); - _serialD->flush(); - if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, LOW); - } - unsigned long startMillis = millis(); - if (!_serialD->available()) { - if (waitReceive == true && _waitCounter > _waitA) { - flagOK = false; - } else waitReceive = true; - } - uint16_t len = 0; - unsigned long startMicros = micros(); - do { - if (_serialD->available()) { - startMicros = micros(); - _currentNode->_analogueInputBuffer[len] = _serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < _currentNode->_numAnaloguePins * 2); - if (!crcGood(_currentNode->_digitalInputStates,sizeof(_currentNode->_digitalInputStates)-2)) DIAG(F("MB CRC error on node %d"), _currentNode->getNodeID()); - - _lastAnalogueRead = currentMicros; - _readState = RDS_ANALOGUE; -} - _currentNode = _currentNode->getNext(); + switch (_refreshOperation) { + case 0: + if (_currentNode->_numDigitalPins>0 && currentMicros - _lastDigitalRead > _digitalRefresh) { // Delay for digital read refresh + // Issue new read request for digital states. As the request is non-blocking, the buffer has to + // be allocated from heap (object state). + _currentNode->_readCommandBuffer[0] = EXIORDD; + _currentNode->_readCommandBuffer[1] = _currentNode->getNodeID(); + updateCrc(_currentNode->_readCommandBuffer,sizeof(_currentNode->_readCommandBuffer)-2); + if (waitReceive == false) { + if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); + _serialD->write(_currentNode->_readCommandBuffer, sizeof(_currentNode->_readCommandBuffer)); + _serialD->flush(); + if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, LOW); + } + unsigned long startMillis = millis(); + if (!_serialD->available()) { + if (waitReceive == true && _waitCounter > _waitA) { + flagOK = false; + } else waitReceive = true; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (_serialD->available()) { + startMicros = micros(); + _currentNode->_digitalInputStates[len] = _serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < (_currentNode->_numDigitalPins+7)/8); + if (!crcGood(_currentNode->_digitalInputStates,sizeof(_currentNode->_digitalInputStates)-2)) { + DIAG(F("EX-IOExpander485 CRC error on node %d"), _currentNode->getNodeID()); + flagOK = false; + } + if (!waitReceive) _refreshOperation++; + _lastDigitalRead = currentMicros; + _readState = RDS_DIGITAL; + } + break; + case 1: + if (_currentNode->_numAnaloguePins>0 && currentMicros - _lastAnalogueRead > _analogueRefresh) { // Delay for analogue read refresh + // Issue new read for analogue input states + _currentNode->_readCommandBuffer[0] = EXIORDAN; + _currentNode->_readCommandBuffer[1] = _currentNode->getNodeID(); + updateCrc(_currentNode->_readCommandBuffer,sizeof(_currentNode->_readCommandBuffer)-2); + if (waitReceive == false) { + if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); + _serialD->write(_currentNode->_readCommandBuffer, sizeof(_currentNode->_readCommandBuffer)); + _serialD->flush(); + if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, LOW); + } + unsigned long startMillis = millis(); + if (!_serialD->available()) { + if (waitReceive == true && _waitCounter > _waitA) { + flagOK = false; + } else waitReceive = true; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (_serialD->available()) { + startMicros = micros(); + _currentNode->_analogueInputBuffer[len] = _serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < _currentNode->_numAnaloguePins * 2); + if (!crcGood(_currentNode->_digitalInputStates,sizeof(_currentNode->_digitalInputStates)-2)) { + DIAG(F("EX-IOExpander485 CRC error on node %d"), _currentNode->getNodeID()); + flagOK = false; + } + if (!waitReceive) _refreshOperation = 0; + _lastAnalogueRead = currentMicros; + _readState = RDS_ANALOGUE; + } + break; + } + if(flagOK == true) _currentNode = _currentNode->getNext(); } #if defined(RS485_STM_OK) @@ -463,7 +389,7 @@ if (taskCnt > 0) { } -// Link to chain of RS485 instances +// Link to chain of RS485 instances, left over from RS485 template. RS485 *RS485::_busList = NULL; @@ -471,11 +397,14 @@ RS485 *RS485::_busList = NULL; * RS485node implementation ************************************************************/ +/* -= RS485node =- +// // Constructor for RS485node object -RS485node::RS485node(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID) { +*/ +RS485node::RS485node(VPIN firstVpin, int nPins, uint8_t nodeID) { _firstVpin = firstVpin; _nPins = nPins; - _busNo = busNo; + _busNo = 0; _nodeID = nodeID; if (_nodeID > 255) _nodeID = 255; diff --git a/IO_RS485.h b/IO_RS485.h index 41e1d79..e562959 100644 --- a/IO_RS485.h +++ b/IO_RS485.h @@ -22,32 +22,28 @@ * RS485 * ======= * To define a RS485, example syntax: - * RS485::create(bus, serial, baud[, cycletime[, pin]]); + * RS485::create(serial, baud[, cycletime[, pin]]); * - * bus = 0-255 * serial = serial port to be used (e.g. Serial3) * baud = baud rate (9600, 19200, 28800, 57600 or 115200) * cycletime = minimum time between successive updates/reads of a node in millisecs (default 500ms) * pin = pin number connected to RS485 module's DE and !RE terminals for half-duplex operation (default VPIN_NONE) - * - * Each bus must use a different serial port. + * * * RS485Node * ======== - * To define a CMRI node and associate it with a CMRI bus, - * CMRInode::create(firstVPIN, numVPINs, bus, nodeID, type [, inputs, outputs]); + * To define a RS485 node and associate it with a RS485 bus, + * RS485node::create(firstVPIN, numVPINs, nodeID); * * firstVPIN = first vpin in block allocated to this device - * numVPINs = number of vpins (e.g. 72 for an SMINI node) - * bus = 0-255 - * nodeID = 0-127 + * numVPINs = number of vpins + * nodeID = 0-255 */ #ifndef IO_RS485_H #define IO_RS485_H #include "IODevice.h" -uint16_t div8RndUp(uint16_t value); /********************************************************************** * RS485node class @@ -101,16 +97,16 @@ public: uint8_t* _digitalInputStates = NULL; uint8_t* _analogueInputStates = NULL; uint8_t* _analogueInputBuffer = NULL; // buffer for I2C input transfers - uint8_t _readCommandBuffer[1]; + uint8_t _readCommandBuffer[4]; uint8_t _digitalPinBytes = 0; // Size of allocated memory buffer (may be longer than needed) uint8_t _analoguePinBytes = 0; // Size of allocated memory buffer (may be longer than needed) uint8_t* _analoguePinMap = NULL; - static void create(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID) { - if (checkNoOverlap(firstVpin, nPins)) new RS485node(firstVpin, nPins, busNo, nodeID); + static void create(VPIN firstVpin, int nPins, uint8_t nodeID) { + if (checkNoOverlap(firstVpin, nPins)) new RS485node(firstVpin, nPins, nodeID); } - RS485node(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID); + RS485node(VPIN firstVpin, int nPins, uint8_t nodeID); uint8_t getNodeID() { return _nodeID; @@ -133,48 +129,48 @@ public: if (paramCount != 1) return false; int pin = vpin - _firstVpin; int pin = vpin - _firstVpin; - RS485* mb = RS485::findBus(0); + RS485 *bus = RS485::findBus(_busNo); int* param[] = {(int*)pin, (int*)configType, (int*)paramCount, (int*)params[0]}; - mb->addTask(_nodeID, 3, 4, param); + bus->addTask(_nodeID, 3, 4, param); } int _configureAnalogIn(VPIN vpin) override { int pin = vpin - _firstVpin; - RS485* mb = RS485::findBus(0); + RS485 *bus = RS485::findBus(_busNo); int* params[] = {(int*)pin}; - mb->addTask(_nodeID, 3, 1, params); + bus->addTask(_nodeID, 3, 1, params); return false; } void _begin() override { - RS485* mb = RS485::findBus(0); - if (mb->_txPin != VPIN_NONE) { - pinMode(mb->_txPin, OUTPUT); - ArduinoPins::fastWriteDigital(mb->_txPin, LOW); + RS485 *bus = RS485::findBus(_busNo); + if (bus->_txPin != VPIN_NONE) { + pinMode(bus->_txPin, OUTPUT); + ArduinoPins::fastWriteDigital(bus->_txPin, LOW); } uint8_t receiveBuffer[5]; uint8_t commandBuffer[7] = {EXIOINIT, _nodeID, (uint8_t)_nPins, (uint8_t)(_firstVpin & 0xFF), (uint8_t)(_firstVpin >> 8)}; - mb->updateCrc(commandBuffer,sizeof(commandBuffer)-2); - if (mb->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(mb->_txPin, HIGH); - mb->_serialD->write(commandBuffer, sizeof(commandBuffer)); - mb->_serialD->flush(); - if (mb->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(mb->_txPin, LOW); + bus->updateCrc(commandBuffer,5); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); + bus->_serialD->write(commandBuffer, 7); + bus->_serialD->flush(); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); unsigned long startMillis = millis(); - while (!mb->_serialD->available()) { + while (!bus->_serialD->available()) { if (millis() - startMillis >= 500) return; } uint16_t len = 0; unsigned long startMicros = micros(); do { - if (mb->_serialD->available()) { + if (bus->_serialD->available()) { startMicros = micros(); - receiveBuffer[len] = mb->_serialD->read(); + receiveBuffer[len] = bus->_serialD->read(); len++; } } while (micros() - startMicros <= 500 && len < 256); - if (receiveBuffer[0] == EXIOPINS && mb->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { + if (receiveBuffer[0] == EXIOPINS && bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { _numDigitalPins = receiveBuffer[1]; _numAnaloguePins = receiveBuffer[2]; @@ -187,7 +183,7 @@ public: if ((_digitalInputStates = (byte*) calloc(digitalBytesNeeded, 1)) != NULL) { _digitalPinBytes = digitalBytesNeeded; } else { - DIAG(F("EX-IOExpanderMB node:%d ERROR alloc %d bytes"), _nodeID, digitalBytesNeeded); + DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), _nodeID, digitalBytesNeeded); _deviceState = DEVSTATE_FAILED; _digitalPinBytes = 0; return; @@ -212,7 +208,7 @@ public: _analoguePinMap != NULL) { _analoguePinBytes = analogueBytesNeeded; } else { - DIAG(F("EX-IOExpanderMB node:%d ERROR alloc analog pin bytes"), _nodeID); + DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), _nodeID); _deviceState = DEVSTATE_FAILED; _analoguePinBytes = 0; return; @@ -220,66 +216,62 @@ public: } } } else { - DIAG(F("EX-IOExpanderMB node:%d ERROR configuring device (CRC: %s)"), _nodeID, mb->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)? "PASS":"FAIL"); + DIAG(F("EX-IOExpander485 node:%d ERROR configuring device (CRC: %s)"), _nodeID, bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)? "PASS":"FAIL"); _deviceState = DEVSTATE_FAILED; return; } commandBuffer[0] = EXIOINITA; - mb->updateCrc(commandBuffer,sizeof(commandBuffer)-2); - if (mb->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(mb->_txPin, HIGH); - mb->_serialD->write(commandBuffer, sizeof(commandBuffer)); - mb->_serialD->flush(); - if (mb->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(mb->_txPin, LOW); + commandBuffer[1] = _nodeID; + bus->updateCrc(commandBuffer,2); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); + bus->_serialD->write(commandBuffer, 4); + bus->_serialD->flush(); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); startMillis = millis(); - while (!mb->_serialD->available()) { + while (!bus->_serialD->available()) { if (millis() - startMillis >= 500) return; } uint16_t len = 0; unsigned long startMicros = micros(); do { - if (mb->_serialD->available()) { + if (bus->_serialD->available()) { startMicros = micros(); - receiveBuffer[len] = mb->_serialD->read(); + receiveBuffer[len] = bus->_serialD->read(); len++; } } while (micros() - startMicros <= 500 && len < 256); - if (mb->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { + if (bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { for (int i = 0; i < _numAnaloguePins; i++) { _analoguePinMap[i] = receiveBuffer[i]; } } uint8_t versionBuffer[5]; commandBuffer[0] = EXIOVER; - mb->updateCrc(commandBuffer,sizeof(commandBuffer)-2); - if (mb->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(mb->_txPin, HIGH); - mb->_serialD->write(commandBuffer, sizeof(commandBuffer)); - mb->_serialD->flush(); - if (mb->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(mb->_txPin, LOW); + commandBuffer[1] = _nodeID; + bus->updateCrc(commandBuffer,2); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); + bus->_serialD->write(commandBuffer, 4); + bus->_serialD->flush(); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); startMillis = millis(); - while (!mb->_serialD->available()) { + while (!bus->_serialD->available()) { if (millis() - startMillis >= 500) return; } uint16_t len = 0; unsigned long startMicros = micros(); do { - if (mb->_serialD->available()) { + if (bus->_serialD->available()) { startMicros = micros(); - versionBuffer[len] = mb->_serialD->read(); + versionBuffer[len] = bus->_serialD->read(); len++; } } while (micros() - startMicros <= 500 && len < 256); - if (mb->crcGood(versionBuffer,sizeof(versionBuffer)-2)) { + if (bus->crcGood(versionBuffer,sizeof(versionBuffer)-2)) { _majorVer = versionBuffer[0]; _minorVer = versionBuffer[1]; _patchVer = versionBuffer[2]; - DIAG(F("EX-IOExpander device found, node:%d, Version v%d.%d.%d"), _nodeID, _majorVer, _minorVer, _patchVer); + DIAG(F("EX-IOExpander485 device found, node:%d, Version v%d.%d.%d"), _nodeID, _majorVer, _minorVer, _patchVer); } - - - - - - #ifdef DIAG_IO _display(); #endif @@ -299,9 +291,9 @@ public: void _write(VPIN vpin, int value) override { if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; - RS485* mb = RS485::findBus(0); + RS485 *bus = RS485::findBus(_busNo); int* params[] = {(int*)pin, (int*)value}; - mb->addTask(_nodeID, 3, 2, params); + bus->addTask(_nodeID, 3, 2, params); } int _readAnalogue(VPIN vpin) override { @@ -323,9 +315,9 @@ public: if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; - RS485* mb = RS485::findBus(0); + RS485 *bus = RS485::findBus(_busNo); int* params[] = {(int*)pin, (int*)value, (int*)profile, (int*)duration}; - mb->addTask(_nodeID, 3, 4, params); + bus->addTask(_nodeID, 3, 4, params); } uint8_t getBusNumber() { @@ -333,7 +325,7 @@ public: } void _display() override { - DIAG(F("EX-IOExpander node:%d v%d.%d.%d Vpins %u-%u %S"), _nodeID, _majorVer, _minorVer, _patchVer, (int)_firstVpin, (int)_firstVpin+_nPins-1, _deviceState == DEVSTATE_FAILED ? F("OFFLINE") : F("")); + DIAG(F("EX-IOExpander485 node:%d v%d.%d.%d Vpins %u-%u %S"), _nodeID, _majorVer, _minorVer, _patchVer, (int)_firstVpin, (int)_firstVpin+_nPins-1, _deviceState == DEVSTATE_FAILED ? F("OFFLINE") : F("")); } @@ -351,19 +343,7 @@ class RS485 : public IODevice { private: // Here we define the device-specific variables. uint8_t _busNo; - uint8_t _adu[262]; - uint16_t _calculateCrc(uint8_t *buf, uint16_t len); - uint16_t _getRegister(uint8_t *buf, uint16_t index); - void _setRegister(uint8_t *buf, uint16_t index, uint16_t value); unsigned long _baud; - - RS485node *_nodeListStart = NULL, *_nodeListEnd = NULL; - RS485node *_currentNode = NULL; - uint8_t _exceptionResponse = 0; - uint8_t getExceptionResponse(); - uint16_t _receiveDataIndex = 0; // Index of next data byte to be received. - RS485 *_nextBus = NULL; // Pointer to next bus instance in list. - void setTimeout(unsigned long timeout); unsigned long _cycleStartTime = 0; unsigned long _timeoutStart = 0; unsigned long _cycleTime; // target time between successive read/write cycles, microseconds @@ -372,21 +352,14 @@ private: unsigned long _postDelay; // delay time after transmission before switching off transmitter (in us) unsigned long _byteTransmitTime; // time in us for transmission of one byte int _operationCount = 0; + int _refreshOperation = 0; static RS485 *_busList; // linked list of defined bus instances bool waitReceive = false; int _waitCounter = 0; int _waitCounterB = 0; int _waitA; - int _waitB; -// Helper function for error handling - void reportError(uint8_t status, bool fail=true) { - DIAG(F("EX-IOExpanderMB Node:%d Error"), _currentNode->getNodeID()); - if (fail) - _deviceState = DEVSTATE_FAILED; - } - - + int* taskData[25]; unsigned long _charTimeout; unsigned long _frameTimeout; enum {RDS_IDLE, RDS_DIGITAL, RDS_ANALOGUE}; // Read operation states @@ -396,7 +369,7 @@ private: unsigned long _lastAnalogueRead = 0; const unsigned long _digitalRefresh = 10000UL; // Delay refreshing digital inputs for 10ms const unsigned long _analogueRefresh = 50000UL; // Delay refreshing analogue inputs for 50ms - + int tasks[255][25]; // EX-IOExpander protocol flags enum { @@ -413,8 +386,23 @@ private: EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM) EXIOERR = 0xEF, // Flag we've received an error }; - int tasks[255][25]; + uint16_t _calculateCrc(uint8_t *buf, uint16_t len); + RS485node *_nodeListStart = NULL, *_nodeListEnd = NULL; + RS485node *_currentNode = NULL; + uint8_t _exceptionResponse = 0; + uint8_t getExceptionResponse(); + uint16_t _receiveDataIndex = 0; // Index of next data byte to be received. + RS485 *_nextBus = NULL; // Pointer to next bus instance in list. + void setTimeout(unsigned long timeout); + +// Helper function for error handling + void reportError(uint8_t status, bool fail=true) { + DIAG(F("EX-IOExpander485 Node:%d Error"), _currentNode->getNodeID()); + if (fail) + _deviceState = DEVSTATE_FAILED; + } + void _moveTasks() { // used one in lead, so move forward for (int i = 0; i < taskCnt-1; i++) { @@ -425,7 +413,13 @@ private: taskCnt--; } public: + int _CommMode = 0; + int _opperation = 0; + uint16_t _pullup; + uint16_t _pin; + int8_t _txPin; int taskCnt = 0; + HardwareSerial *_serialD; void addTask(int nodeID, int taskNum, int paramCnt, int *param[]) { taskCnt++; tasks[taskCnt][0] = nodeID; @@ -471,42 +465,14 @@ public: _moveTasks(); } - int8_t _txPin; - uint8_t *rtu = _adu + 6; - uint8_t *tcp = _adu; - uint8_t *pdu = _adu + 7; - uint8_t *data = _adu + 8; + void updateCrc(uint8_t *buf, uint16_t len); bool crcGood(uint8_t *buf, uint16_t len); - uint16_t getLength(); - void setTransactionId(uint16_t transactionId); - void setProtocolId(uint16_t protocolId); - void setLength(uint16_t length); - void setUnitId(uint8_t unitId); - void setFunctionCode(uint8_t functionCode); - void setDataRegister(uint8_t index, uint16_t value); - - void setRtuLen(uint16_t rtuLen); - void setTcpLen(uint16_t tcpLen); - void setPduLen(uint16_t pduLen); - void setDataLen(uint16_t dataLen); - - uint16_t getTransactionId(); - uint16_t getProtocolId(); - - uint8_t getUnitId(); - uint8_t getFunctionCode(); - uint16_t getDataRegister(uint8_t index); - - uint16_t getRtuLen(); - uint16_t getTcpLen(); - uint16_t getPduLen(); - uint16_t getDataLen(); void clearRxBuffer(); - static void create(uint8_t busNo, HardwareSerial& serial, unsigned long baud, uint16_t cycleTimeMS=500, int8_t txPin=-1, int waitA=10, int waitB=10) { - new RS485(busNo, serial, baud, cycleTimeMS, txPin, waitA, waitB); + static void create(HardwareSerial& serial, unsigned long baud, uint16_t cycleTimeMS=500, int8_t txPin=-1, int waitA=10) { + new RS485(serial, baud, cycleTimeMS, txPin, waitA); } - HardwareSerial *_serialD; + // Device-specific initialisation void _begin() override { _serialD->begin(_baud, SERIAL_8N1); @@ -537,17 +503,14 @@ public: _display(); #endif } - int _CommMode = 0; - int _opperation = 0; - uint16_t _pullup; - uint16_t _pin; + // Loop function (overriding IODevice::_loop(unsigned long)) void _loop(unsigned long currentMicros) override; // Display information about the device void _display() override { - DIAG(F("RS485 Configured on Vpins:%d-%d %S"), _firstVpin, _firstVpin+_nPins-1, + DIAG(F("EX-IOExpander485 Configured on Vpins:%d-%d %S"), _firstVpin, _firstVpin+_nPins-1, _deviceState == DEVSTATE_FAILED ? F("OFFLINE") : F("OK")); } @@ -573,7 +536,7 @@ public: } protected: - RS485(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA, int waitB); + RS485(HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA); public: