From b39523bfa6afbf3a557c4de5d9341e4e4cd62789 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sat, 23 Nov 2024 16:15:36 -0500 Subject: [PATCH 01/73] initial commit, compiles, but hangs STM32 --- IO_Modbus.cpp | 114 ++++++++++++++++++++ IO_Modbus.h | 255 ++++++++++++++++++++++++++++++++++++++++++++ ModbusADU.cpp | 153 ++++++++++++++++++++++++++ ModbusADU.h | 52 +++++++++ ModbusRTUComm.cpp | 98 +++++++++++++++++ ModbusRTUComm.h | 33 ++++++ ModbusRTUMaster.cpp | 216 +++++++++++++++++++++++++++++++++++++ ModbusRTUMaster.h | 57 ++++++++++ 8 files changed, 978 insertions(+) create mode 100644 IO_Modbus.cpp create mode 100644 IO_Modbus.h create mode 100644 ModbusADU.cpp create mode 100644 ModbusADU.h create mode 100644 ModbusRTUComm.cpp create mode 100644 ModbusRTUComm.h create mode 100644 ModbusRTUMaster.cpp create mode 100644 ModbusRTUMaster.h diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp new file mode 100644 index 00000000..4c7df514 --- /dev/null +++ b/IO_Modbus.cpp @@ -0,0 +1,114 @@ +/* + * © 2024, Travis Farmer. All rights reserved. + * + * This file is part of DCC++EX API + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + +#include "IO_Modbus.h" +#include "defines.h" + + +/************************************************************ + * Modbus implementation + ************************************************************/ + +// Constructor for Modbus +Modbus::Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_t cycleTimeMS, int16_t transmitEnablePin) { + _busNo = busNo; + _baud = baud; + _serial = &serial; + _cycleTime = cycleTimeMS * 1000UL; // convert from milliseconds to microseconds. + _transmitEnablePin = transmitEnablePin; + if (_transmitEnablePin != VPIN_NONE) { + pinMode(_transmitEnablePin, OUTPUT); + ArduinoPins::fastWriteDigital(_transmitEnablePin, 0); // transmitter initially off + } + ModbusRTUMaster modbusmaster(*_serial, _transmitEnablePin); + + // Add device to HAL device chain + IODevice::addDevice(this); + + // Add bus to CMRIbus chain. + _nextBus = _busList; + _busList = this; + const char* errorStrings[] = {"success", "invalid id", "invalid buffer", "invalid quantity", "response timeout", "frame error", "crc error", "unknown comm error", "unexpected id", "exception response", "unexpected function code", "unexpected response length", "unexpected byte count", "unexpected address", "unexpected value", "unexpected quantity"}; +} + + +// Main loop function for CMRIbus. +// Work through list of nodes. For each node, in separate loop entries +// send initialisation message (once only); then send +// output message; then send prompt for input data, and +// process any response data received. +// When the slot time has finished, move on to the next device. +void Modbus::_loop(unsigned long currentMicros) { + + _currentMicros = currentMicros; + if (_currentNode == NULL) { + // If we're between read/write cycles then don't do anything else. + if (_currentMicros - _cycleStartTime < _cycleTime) return; + // ... otherwise start processing the first node in the list + DIAG(F("Modbusnode: 138 _nodeListEnd:%d "), _nodeListEnd); + DIAG(F("Modbusnode: 139 _currentNode:%d "), _currentNode); + _currentNode = _nodeListStart; + DIAG(F("Modbusnode: 141 _currentNode:%d "), _currentNode); + _cycleStartTime = _currentMicros; + } + if (_currentNode == NULL) return; + + uint8_t error; + error = modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, _currentNode->coils, _currentNode->getNumCoils()); + if (error != 0) DIAG(F("%02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); + + error = modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, _currentNode->discreteInputs, _currentNode->getNumDisInputs()); + if (error != 0) DIAG(F("%02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDisInputs(), errorStrings[error]); +} + +// Link to chain of CMRI bus instances +Modbus *Modbus::_busList = NULL; + + +/************************************************************ + * Modbusnode implementation + ************************************************************/ + +// Constructor for Modbusnode object +Modbusnode::Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils, uint8_t numDiscreteInputs) { + _firstVpin = firstVpin; + _nPins = nPins; + _busNo = busNo; + _nodeID = nodeID; + coils[numCoils]; + discreteInputs[numDiscreteInputs]; + + if ((unsigned int)_nPins < numDiscreteInputs + numCoils) + DIAG(F("Modbusnode: bus:%d nodeID:%d WARNING number of Vpins does not cover all inputs and outputs"), _busNo, _nodeID); + + if (!discreteInputs || !coils) { + DIAG(F("Modbusnode: ERROR insufficient memory")); + return; + } + + // Add this device to HAL device list + IODevice::addDevice(this); + + // Add Modbusnode to Modbus object. + Modbus *bus = Modbus::findBus(_busNo); + if (bus != NULL) { + bus->addNode(this); + return; + } +} diff --git a/IO_Modbus.h b/IO_Modbus.h new file mode 100644 index 00000000..4933eeb6 --- /dev/null +++ b/IO_Modbus.h @@ -0,0 +1,255 @@ +/* + * © 2024, Travis Farmer. All rights reserved. + * + * This file is part of DCC++EX API + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + +/* + * Modbus + * ======= + * To define a Modbus, example syntax: + * Modbus::create(bus, 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. + * + * ModbusNode + * ======== + * To define a CMRI node and associate it with a CMRI bus, + * CMRInode::create(firstVPIN, numVPINs, bus, nodeID, type [, inputs, outputs]); + * + * 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 + * numDiscreteInputs = number of discrete inputs + * numCoils = number of coils + * + * Reference: "LCS-9.10.1 + * Layout Control Specification: CMRInet Protocol + * Version 1.1 December 2014." + */ + +#ifndef IO_MODBUS_H +#define IO_MODBUS_H + +#include "IODevice.h" +#include "ModbusRTUMaster.h" +/********************************************************************** + * Modbusnode class + * + * This encapsulates the state associated with a single Modbus node, + * which includes the nodeID, number of discrete inputs and coils, and + * the states of the discrete inputs and coils. + **********************************************************************/ +class Modbusnode : public IODevice { +private: + uint8_t _busNo; + uint8_t _nodeID; + char _type; + Modbusnode *_next = NULL; + bool _initialised = false; + uint8_t numCoils; + uint8_t numDiscreteInputs; + +public: + static void create(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils=0, uint8_t numDiscreteInputs=0) { + if (checkNoOverlap(firstVpin, nPins)) new Modbusnode(firstVpin, nPins, busNo, nodeID, numCoils, numDiscreteInputs); + } + Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils=0, uint8_t numDiscreteInputs=0); + bool *coils; + bool *discreteInputs; + + uint8_t getNodeID() { + return _nodeID; + } + uint8_t getNumCoils() { + return numCoils; + } + uint8_t getNumDisInputs() { + return numDiscreteInputs; + } + + Modbusnode *getNext() { + return _next; + } + void setNext(Modbusnode *node) { + _next = node; + } + bool isInitialised() { + return _initialised; + } + void setInitialised() { + _initialised = true; + } + + void _begin() { + _initialised = false; + } + + int _read(VPIN vpin) { + // Return current state from this device + uint16_t pin = vpin - _firstVpin; + if (pin < numDiscreteInputs) { + return discreteInputs[pin]; + } else + return 0; + } + + void _write(VPIN vpin, int value) { + // Update current state for this device, in preparation the bus transmission + uint16_t pin = vpin - _firstVpin - numCoils; + if (pin < numCoils) { + if (value) + coils[pin] = value; + else + coils[pin]; + } + } + + void saveIncomingData(uint8_t index, uint8_t data) { + if (index < numDiscreteInputs) + discreteInputs[index] = data; + } + + uint8_t getOutputStates(uint8_t index) { + if (index < numCoils) + return coils[index]; + else + return 0; + } + + uint16_t getNumInputs() { + return numDiscreteInputs; + } + + uint16_t getNumOutputs() { + return numCoils; + } + + char getType() { + return _type; + } + + uint8_t getBusNumber() { + return _busNo; + } + + void _display() override { + DIAG(F("Modbusnode type:'%c' configured on bus:%d nodeID:%d VPINs:%u-%u (in) %u-%u (out)"), + _type, _busNo, _nodeID, _firstVpin, _firstVpin+numDiscreteInputs-1, + _firstVpin+numDiscreteInputs, _firstVpin+numDiscreteInputs+numCoils-1); + } + +}; + +/********************************************************************** + * Modbus class + * + * This encapsulates the properties state of the bus and the + * transmission and reception of data across that bus. Each Modbus + * object owns a set of Modbusnode objects which represent the nodes + * attached to that bus. + **********************************************************************/ +class Modbus : public IODevice { +private: + // Here we define the device-specific variables. + uint8_t _busNo; + + unsigned long _baud; + int16_t _transmitEnablePin = VPIN_NONE; + Modbusnode *_nodeListStart = NULL, *_nodeListEnd = NULL; + Modbusnode *_currentNode = NULL; + + uint16_t _receiveDataIndex = 0; // Index of next data byte to be received. + Modbus *_nextBus = NULL; // Pointer to next bus instance in list. + unsigned long _cycleStartTime = 0; + unsigned long _timeoutStart = 0; + unsigned long _cycleTime; // target time between successive read/write cycles, microseconds + unsigned long _timeoutPeriod; // timeout on read responses, in microseconds. + unsigned long _currentMicros; // last value of micros() from _loop function. + unsigned long _postDelay; // delay time after transmission before switching off transmitter (in us) + unsigned long _byteTransmitTime; // time in us for transmission of one byte + + static Modbus *_busList; // linked list of defined bus instances + +public: + static void create(uint8_t busNo, HardwareSerial& serial, unsigned long baud, uint16_t cycleTimeMS=500, int16_t transmitEnablePin=VPIN_NONE) { + new Modbus(busNo, serial, baud, cycleTimeMS, transmitEnablePin); + } + HardwareSerial *_serial; + ModbusRTUMaster *modbusmaster; + const char* errorStrings[]; + // Device-specific initialisation + void _begin() override { + _serial->begin(_baud, SERIAL_8N1); + modbusmaster->begin(_baud); + #if defined(DIAG_IO) + _display(); + #endif + } + + // Loop function (overriding IODevice::_loop(unsigned long)) + void _loop(unsigned long currentMicros) override; + + // Display information about the device + void _display() override { + DIAG(F("Modbus %d configured, speed=%d baud, cycle=%d ms"), _busNo, _baud, _cycleTime/1000); + } + + // Locate Modbusnode object with specified nodeID. + Modbusnode *findNode(uint8_t nodeID) { + for (Modbusnode *node = _nodeListStart; node != NULL; node = node->getNext()) { + if (node->getNodeID() == nodeID) + return node; + } + return NULL; + } + + // Add new Modbusnode to the list of nodes for this bus. + void addNode(Modbusnode *newNode) { + if (!_nodeListStart) + _nodeListStart = newNode; + if (!_nodeListEnd) + _nodeListEnd = newNode; + else + _nodeListEnd->setNext(newNode); + DIAG(F("bus: 260h nodeID: _nodeListStart:%d _nodeListEnd:%d"), _nodeListStart, _nodeListEnd); + } + +protected: + Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_t cycleTimeMS, int16_t transmitEnablePin); + +public: + + uint8_t getBusNumber() { + return _busNo; + } + + static Modbus *findBus(uint8_t busNo) { + for (Modbus *bus=_busList; bus!=NULL; bus=bus->_nextBus) { + if (bus->_busNo == busNo) return bus; + } + return NULL; + } +}; + +#endif // IO_MODBUS_H diff --git a/ModbusADU.cpp b/ModbusADU.cpp new file mode 100644 index 00000000..b7752b28 --- /dev/null +++ b/ModbusADU.cpp @@ -0,0 +1,153 @@ +#include "ModbusADU.h" + +void ModbusADU::setTransactionId(uint16_t transactionId) { + _setRegister(tcp, 0, transactionId); +} + +void ModbusADU::setProtocolId(uint16_t protocolId) { + _setRegister(tcp, 2, protocolId); +} + +void ModbusADU::setLength(uint16_t length) { + if (length < 3 || length > 254) _setRegister(tcp, 4, 0); + else _setRegister(tcp, 4, length); +} + +void ModbusADU::setUnitId(uint8_t unitId) { + tcp[6] = unitId; +} + +void ModbusADU::setFunctionCode(uint8_t functionCode) { + pdu[0] = functionCode; +} + +void ModbusADU::setDataRegister(uint8_t index, uint16_t value) { + _setRegister(data, index, value); +} + + + +void ModbusADU::setRtuLen(uint16_t rtuLen) { + setLength(rtuLen - 2); +} + +void ModbusADU::setTcpLen(uint16_t tcpLen) { + setLength(tcpLen - 6); +} + +void ModbusADU::setPduLen(uint16_t pduLen) { + setLength(pduLen + 1); +} + +void ModbusADU::setDataLen(uint16_t dataLen) { + setLength(dataLen + 2); +} + + + +uint16_t ModbusADU::getTransactionId() { + return _getRegister(tcp, 0); +} + +uint16_t ModbusADU::getProtocolId() { + return _getRegister(tcp, 2); +} + +uint16_t ModbusADU::getLength() { + uint16_t length = _getRegister(tcp, 4); + if (length < 3 || length > 254) return 0; + else return length; +} + +uint8_t ModbusADU::getUnitId() { + return tcp[6]; +} + +uint8_t ModbusADU::getFunctionCode() { + return pdu[0]; +} + +uint16_t ModbusADU::getDataRegister(uint8_t index) { + return _getRegister(data, index); +} + + + +uint16_t ModbusADU::getRtuLen() { + uint16_t len = getLength(); + if (len == 0) return 0; + else return len + 2; +} + +uint16_t ModbusADU::getTcpLen() { + uint16_t len = getLength(); + if (len == 0) return 0; + else return len + 6; +} + +uint16_t ModbusADU::getPduLen() { + uint16_t len = getLength(); + if (len == 0) return 0; + else return len - 1; +} + +uint16_t ModbusADU::getDataLen() { + uint16_t len = getLength(); + if (len == 0) return 0; + else return len - 2; +} + + + +void ModbusADU::updateCrc() { + uint16_t len = getLength(); + uint16_t crc = _calculateCrc(len); + rtu[len] = lowByte(crc); + rtu[len + 1] = highByte(crc); +} + +bool ModbusADU::crcGood() { + uint16_t len = getLength(); + uint16_t aduCrc = rtu[len] | (rtu[len + 1] << 8); + uint16_t calculatedCrc = _calculateCrc(len); + if (aduCrc == calculatedCrc) return true; + else return false; +} + + + +void ModbusADU::prepareExceptionResponse(uint8_t exceptionCode) { + pdu[0] |= 0x80; + pdu[1] = exceptionCode; + setPduLen(2); +} + + + +void ModbusADU::_setRegister(uint8_t *buf, uint16_t index, uint16_t value) { + buf[index] = highByte(value); + buf[index + 1] = lowByte(value); +} + +uint16_t ModbusADU::_getRegister(uint8_t *buf, uint16_t index) { + return (buf[index] << 8) | buf[index + 1]; +} + +uint16_t ModbusADU::_calculateCrc(uint16_t len) { + uint16_t value = 0xFFFF; + for (uint16_t i = 0; i < len; i++) { + value ^= (uint16_t)rtu[i]; + for (uint8_t j = 0; j < 8; j++) { + bool lsb = value & 1; + value >>= 1; + if (lsb == true) value ^= 0xA001; + } + } + return value; +} + + + +uint16_t div8RndUp(uint16_t value) { + return (value + 7) >> 3; +} \ No newline at end of file diff --git a/ModbusADU.h b/ModbusADU.h new file mode 100644 index 00000000..2aa72d96 --- /dev/null +++ b/ModbusADU.h @@ -0,0 +1,52 @@ +#ifndef ModbusADU_h +#define ModbusADU_h + +#include "Arduino.h" + +class ModbusADU { + public: + uint8_t *rtu = _adu + 6; + uint8_t *tcp = _adu; + uint8_t *pdu = _adu + 7; + uint8_t *data = _adu + 8; + + 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(); + uint16_t getLength(); + 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 updateCrc(); + bool crcGood(); + + void prepareExceptionResponse(uint8_t exceptionCode); + + private: + uint8_t _adu[262]; + void _setRegister(uint8_t *buf, uint16_t index, uint16_t value); + uint16_t _getRegister(uint8_t *buf, uint16_t index); + uint16_t _calculateCrc(uint16_t len); + +}; + +uint16_t div8RndUp(uint16_t value); + +#endif \ No newline at end of file diff --git a/ModbusRTUComm.cpp b/ModbusRTUComm.cpp new file mode 100644 index 00000000..2abbe960 --- /dev/null +++ b/ModbusRTUComm.cpp @@ -0,0 +1,98 @@ +#include "ModbusRTUComm.h" + +ModbusRTUComm::ModbusRTUComm(Stream& serial, int8_t dePin, int8_t rePin) : _serial(serial) { + _dePin = dePin; + _rePin = rePin; +} + +void ModbusRTUComm::begin(unsigned long baud, uint32_t config) { + unsigned long bitsPerChar; + switch (config) { + case SERIAL_8E2: + case SERIAL_8O2: + bitsPerChar = 12; + break; + case SERIAL_8N2: + case SERIAL_8E1: + case SERIAL_8O1: + bitsPerChar = 11; + break; + case SERIAL_8N1: + default: + bitsPerChar = 10; + break; + } + if (baud <= 19200) { + _charTimeout = (bitsPerChar * 2500000) / baud; + _frameTimeout = (bitsPerChar * 4500000) / baud; + } + else { + _charTimeout = (bitsPerChar * 1000000) / baud + 750; + _frameTimeout = (bitsPerChar * 1000000) / baud + 1750; + } + #if defined(ARDUINO_UNOR4_MINIMA) || defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_GIGA) || (defined(ARDUINO_NANO_RP2040_CONNECT) && defined(ARDUINO_ARCH_MBED)) + _postDelay = ((bitsPerChar * 1000000) / baud) + 2; + #endif + if (_dePin >= 0) { + pinMode(_dePin, OUTPUT); + digitalWrite(_dePin, LOW); + } + if (_rePin >= 0) { + pinMode(_rePin, OUTPUT); + digitalWrite(_rePin, LOW); + } + clearRxBuffer(); +} + +void ModbusRTUComm::setTimeout(unsigned long timeout) { + _readTimeout = timeout; +} + +ModbusRTUCommError ModbusRTUComm::readAdu(ModbusADU& adu) { + adu.setRtuLen(0); + unsigned long startMillis = millis(); + while (!_serial.available()) { + if (millis() - startMillis >= _readTimeout) return MODBUS_RTU_COMM_TIMEOUT; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (_serial.available()) { + startMicros = micros(); + adu.rtu[len] = _serial.read(); + len++; + } + } while (micros() - startMicros <= _charTimeout && len < 256); + adu.setRtuLen(len); + while (micros() - startMicros < _frameTimeout); + if (_serial.available()) { + adu.setRtuLen(0); + return MODBUS_RTU_COMM_FRAME_ERROR; + } + if (!adu.crcGood()) { + adu.setRtuLen(0); + return MODBUS_RTU_COMM_CRC_ERROR; + } + return MODBUS_RTU_COMM_SUCCESS; +} + +void ModbusRTUComm::writeAdu(ModbusADU& adu) { + adu.updateCrc(); + if (_dePin >= 0) digitalWrite(_dePin, HIGH); + if (_rePin >= 0) digitalWrite(_rePin, HIGH); + _serial.write(adu.rtu, adu.getRtuLen()); + _serial.flush(); + delayMicroseconds(_postDelay); + if (_dePin >= 0) digitalWrite(_dePin, LOW); + if (_rePin >= 0) digitalWrite(_rePin, LOW); +} + +void ModbusRTUComm::clearRxBuffer() { + unsigned long startMicros = micros(); + do { + if (_serial.available() > 0) { + startMicros = micros(); + _serial.read(); + } + } while (micros() - startMicros < _frameTimeout); +} diff --git a/ModbusRTUComm.h b/ModbusRTUComm.h new file mode 100644 index 00000000..cae72341 --- /dev/null +++ b/ModbusRTUComm.h @@ -0,0 +1,33 @@ +#ifndef ModbusRTUComm_h +#define ModbusRTUComm_h + +#include "Arduino.h" +#include "ModbusADU.h" + +enum ModbusRTUCommError : uint8_t { + MODBUS_RTU_COMM_SUCCESS = 0, + MODBUS_RTU_COMM_TIMEOUT = 1, + MODBUS_RTU_COMM_FRAME_ERROR = 2, + MODBUS_RTU_COMM_CRC_ERROR = 3 +}; + +class ModbusRTUComm { + public: + ModbusRTUComm(Stream& serial, int8_t dePin = -1, int8_t rePin = -1); + void begin(unsigned long baud, uint32_t config = SERIAL_8N1); + void setTimeout(unsigned long timeout); + ModbusRTUCommError readAdu(ModbusADU& adu); + void writeAdu(ModbusADU& adu); + void clearRxBuffer(); + + private: + Stream& _serial; + int8_t _dePin; + int8_t _rePin; + unsigned long _charTimeout; + unsigned long _frameTimeout; + unsigned long _postDelay = 0; + unsigned long _readTimeout = 0; +}; + +#endif \ No newline at end of file diff --git a/ModbusRTUMaster.cpp b/ModbusRTUMaster.cpp new file mode 100644 index 00000000..897ce5ae --- /dev/null +++ b/ModbusRTUMaster.cpp @@ -0,0 +1,216 @@ +#include "ModbusRTUMaster.h" + +ModbusRTUMaster::ModbusRTUMaster(Stream& serial, int8_t dePin, int8_t rePin) : _rtuComm(serial, dePin, rePin) { + _rtuComm.setTimeout(500); +} + +void ModbusRTUMaster::setTimeout(unsigned long timeout) { + _rtuComm.setTimeout(timeout); +} + +void ModbusRTUMaster::begin(unsigned long baud, uint32_t config) { + _rtuComm.begin(baud, config); +} + + + +ModbusRTUMasterError ModbusRTUMaster::readCoils(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity) { + return _readValues(id, 1, startAddress, buf, quantity); +} + +ModbusRTUMasterError ModbusRTUMaster::readDiscreteInputs(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity) { + return _readValues(id, 2, startAddress, buf, quantity); +} + +ModbusRTUMasterError ModbusRTUMaster::readHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { + return _readValues(id, 3, startAddress, buf, quantity); +} + +ModbusRTUMasterError ModbusRTUMaster::readInputRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { + return _readValues(id, 4, startAddress, buf, quantity); +} + + + +ModbusRTUMasterError ModbusRTUMaster::writeSingleCoil(uint8_t id, uint16_t address, bool value) { + return _writeSingleValue(id, 5, address, ((value) ? 0xFF00 : 0x0000)); +} + +ModbusRTUMasterError ModbusRTUMaster::writeSingleHoldingRegister(uint8_t id, uint16_t address, uint16_t value) { + return _writeSingleValue(id, 6, address, value); +} + + + +ModbusRTUMasterError ModbusRTUMaster::writeMultipleCoils(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity) { + const uint8_t functionCode = 15; + if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; + if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; + if (quantity == 0 || quantity > 1968) return MODBUS_RTU_MASTER_INVALID_QUANTITY; + ModbusADU adu; + uint16_t byteCount = div8RndUp(quantity); + adu.setUnitId(id); + adu.setFunctionCode(functionCode); + adu.setDataRegister(0, startAddress); + adu.setDataRegister(2, quantity); + adu.data[4] = byteCount; + for (uint16_t i = 0; i < quantity; i++) { + bitWrite(adu.data[5 + (i >> 3)], i & 7, buf[i]); + } + for (uint16_t i = quantity; i < (byteCount * 8); i++) { + bitClear(adu.data[5 + (i >> 3)], i & 7); + } + adu.setDataLen(5 + byteCount); + _rtuComm.writeAdu(adu); + if (id == 0) return MODBUS_RTU_MASTER_SUCCESS; + ModbusRTUCommError commError = _rtuComm.readAdu(adu); + if (commError) return _translateCommError(commError); + if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; + if (adu.getFunctionCode() == (functionCode + 0x80)) { + _exceptionResponse = adu.data[0]; + return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; + } + if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; + if (adu.getDataLen() != 4) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; + if (adu.getDataRegister(0) != startAddress) return MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS; + if (adu.getDataRegister(2) != quantity) return MODBUS_RTU_MASTER_UNEXPECTED_QUANTITY; + return MODBUS_RTU_MASTER_SUCCESS; +} + +ModbusRTUMasterError ModbusRTUMaster::writeMultipleHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { + uint8_t functionCode = 16; + if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; + if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; + if (quantity == 0 || quantity > 123) return MODBUS_RTU_MASTER_INVALID_QUANTITY; + uint16_t byteCount = quantity * 2; + ModbusADU adu; + adu.setUnitId(id); + adu.setFunctionCode(functionCode); + adu.setDataRegister(0, startAddress); + adu.setDataRegister(2, quantity); + adu.data[4] = byteCount; + for (uint16_t i = 0; i < quantity; i++) { + adu.setDataRegister(5 + (i * 2), buf[i]); + } + adu.setDataLen(5 + byteCount); + _rtuComm.writeAdu(adu); + if (id == 0) return MODBUS_RTU_MASTER_SUCCESS; + ModbusRTUCommError commError = _rtuComm.readAdu(adu); + if (commError) return _translateCommError(commError); + if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; + if (adu.getFunctionCode() == (functionCode + 0x80)) { + _exceptionResponse = adu.data[0]; + return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; + } + if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; + if (adu.getDataLen() != 4) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; + if (adu.getDataRegister(0) != startAddress) return MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS; + if (adu.getDataRegister(2) != quantity) return MODBUS_RTU_MASTER_UNEXPECTED_QUANTITY; + return MODBUS_RTU_MASTER_SUCCESS; +} + + + +uint8_t ModbusRTUMaster::getExceptionResponse() { + return _exceptionResponse; +} + + + +ModbusRTUMasterError ModbusRTUMaster::_readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, bool buf[], uint16_t quantity) { + if (id < 1 || id > 247) return MODBUS_RTU_MASTER_INVALID_ID; + if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; + if (quantity == 0 || quantity > 2000) return MODBUS_RTU_MASTER_INVALID_QUANTITY; + ModbusADU adu; + adu.setUnitId(id); + adu.setFunctionCode(functionCode); + adu.setDataRegister(0, startAddress); + adu.setDataRegister(2, quantity); + adu.setDataLen(4); + _rtuComm.writeAdu(adu); + ModbusRTUCommError commError = _rtuComm.readAdu(adu); + if (commError) return _translateCommError(commError); + if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; + if (adu.getFunctionCode() == (functionCode + 0x80)) { + _exceptionResponse = adu.data[0]; + return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; + } + if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; + uint16_t byteCount = div8RndUp(quantity); + if (adu.getDataLen() != (1 + byteCount)) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; + if (adu.data[0] != byteCount) return MODBUS_RTU_MASTER_UNEXPECTED_BYTE_COUNT; + for (uint16_t i = 0; i < quantity; i++) { + buf[i] = bitRead(adu.data[1 + (i >> 3)], i & 7); + } + return MODBUS_RTU_MASTER_SUCCESS; +} + +ModbusRTUMasterError ModbusRTUMaster::_readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { + if (id < 1 || id > 247) return MODBUS_RTU_MASTER_INVALID_ID; + if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; + if (quantity == 0 || quantity > 125) return MODBUS_RTU_MASTER_INVALID_QUANTITY; + ModbusADU adu; + adu.setUnitId(id); + adu.setFunctionCode(functionCode); + adu.setDataRegister(0, startAddress); + adu.setDataRegister(2, quantity); + adu.setDataLen(4); + _rtuComm.writeAdu(adu); + ModbusRTUCommError commError = _rtuComm.readAdu(adu); + if (commError) return _translateCommError(commError); + if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; + if (adu.getFunctionCode() == (functionCode + 0x80)) { + _exceptionResponse = adu.data[0]; + return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; + } + if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; + uint16_t byteCount = quantity * 2; + if (adu.getDataLen() != (1 + byteCount)) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; + if (adu.data[0] != byteCount) return MODBUS_RTU_MASTER_UNEXPECTED_BYTE_COUNT; + for (uint16_t i = 0; i < quantity; i++) { + buf[i] = adu.getDataRegister(1 + (i * 2)); + } + return MODBUS_RTU_MASTER_SUCCESS; +} + +ModbusRTUMasterError ModbusRTUMaster::_writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value) { + if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; + ModbusADU adu; + adu.setUnitId(id); + adu.setFunctionCode(functionCode); + adu.setDataRegister(0, address); + adu.setDataRegister(2, value); + adu.setDataLen(4); + _rtuComm.writeAdu(adu); + if (id == 0) return MODBUS_RTU_MASTER_SUCCESS; + ModbusRTUCommError commError = _rtuComm.readAdu(adu); + if (commError) return _translateCommError(commError); + if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; + if (adu.getFunctionCode() == (functionCode + 0x80)) { + _exceptionResponse = adu.data[0]; + return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; + } + if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; + if (adu.getDataLen() != 4) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; + if (adu.getDataRegister(0) != address) return MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS; + if (adu.getDataRegister(2) != value) return MODBUS_RTU_MASTER_UNEXPECTED_VALUE; + return MODBUS_RTU_MASTER_SUCCESS; +} + + + +ModbusRTUMasterError ModbusRTUMaster::_translateCommError(ModbusRTUCommError commError) { + switch (commError) { + case MODBUS_RTU_COMM_SUCCESS: + return MODBUS_RTU_MASTER_SUCCESS; + case MODBUS_RTU_COMM_TIMEOUT: + return MODBUS_RTU_MASTER_RESPONSE_TIMEOUT; + case MODBUS_RTU_COMM_FRAME_ERROR: + return MODBUS_RTU_MASTER_FRAME_ERROR; + case MODBUS_RTU_COMM_CRC_ERROR: + return MODBUS_RTU_MASTER_CRC_ERROR; + default: + return MODBUS_RTU_MASTER_UNKNOWN_COMM_ERROR; + } +} + diff --git a/ModbusRTUMaster.h b/ModbusRTUMaster.h new file mode 100644 index 00000000..d9193c35 --- /dev/null +++ b/ModbusRTUMaster.h @@ -0,0 +1,57 @@ +#ifndef ModbusRTUMaster_h +#define ModbusRTUMaster_h + +#include "Arduino.h" +#include "ModbusADU.h" +#include "ModbusRTUComm.h" + +enum ModbusRTUMasterError : uint8_t { + MODBUS_RTU_MASTER_SUCCESS = 0, + MODBUS_RTU_MASTER_INVALID_ID = 1, + MODBUS_RTU_MASTER_INVALID_BUFFER = 2, + MODBUS_RTU_MASTER_INVALID_QUANTITY = 3, + MODBUS_RTU_MASTER_RESPONSE_TIMEOUT = 4, + MODBUS_RTU_MASTER_FRAME_ERROR = 5, + MODBUS_RTU_MASTER_CRC_ERROR = 6, + MODBUS_RTU_MASTER_UNKNOWN_COMM_ERROR = 7, + MODBUS_RTU_MASTER_UNEXPECTED_ID = 8, + MODBUS_RTU_MASTER_EXCEPTION_RESPONSE = 9, + MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE = 10, + MODBUS_RTU_MASTER_UNEXPECTED_LENGTH = 11, + MODBUS_RTU_MASTER_UNEXPECTED_BYTE_COUNT = 12, + MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS = 13, + MODBUS_RTU_MASTER_UNEXPECTED_VALUE = 14, + MODBUS_RTU_MASTER_UNEXPECTED_QUANTITY = 15 +}; + +class ModbusRTUMaster { + public: + ModbusRTUMaster(Stream& serial, int8_t dePin = -1, int8_t rePin = -1); + void setTimeout(unsigned long timeout); + void begin(unsigned long baud, uint32_t config = SERIAL_8N1); + + ModbusRTUMasterError readCoils(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity); + ModbusRTUMasterError readDiscreteInputs(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity); + ModbusRTUMasterError readHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); + ModbusRTUMasterError readInputRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); + + ModbusRTUMasterError writeSingleCoil(uint8_t id, uint16_t address, bool value); + ModbusRTUMasterError writeSingleHoldingRegister(uint8_t id, uint16_t address, uint16_t value); + ModbusRTUMasterError writeMultipleCoils(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity); + ModbusRTUMasterError writeMultipleHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); + + uint8_t getExceptionResponse(); + + private: + ModbusRTUComm _rtuComm; + uint8_t _exceptionResponse = 0; + + ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, bool buf[], uint16_t quantity); + ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity); + ModbusRTUMasterError _writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value); + + ModbusRTUMasterError _translateCommError(ModbusRTUCommError commError); + +}; + +#endif From 755e6ab0cc888a8e9e80495ed59ab417141a8869 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sun, 24 Nov 2024 04:39:52 -0500 Subject: [PATCH 02/73] squish together IO_Modbus --- IO_Modbus.cpp | 461 ++++++++++++++++++++++++++++++++++++++++++++ IO_Modbus.h | 124 +++++++++++- ModbusADU.cpp | 153 --------------- ModbusADU.h | 52 ----- ModbusRTUComm.cpp | 98 ---------- ModbusRTUComm.h | 33 ---- ModbusRTUMaster.cpp | 216 --------------------- ModbusRTUMaster.h | 57 ------ 8 files changed, 584 insertions(+), 610 deletions(-) delete mode 100644 ModbusADU.cpp delete mode 100644 ModbusADU.h delete mode 100644 ModbusRTUComm.cpp delete mode 100644 ModbusRTUComm.h delete mode 100644 ModbusRTUMaster.cpp delete mode 100644 ModbusRTUMaster.h diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 4c7df514..7404b12c 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -19,7 +19,468 @@ #include "IO_Modbus.h" #include "defines.h" +void ModbusADU::setTransactionId(uint16_t transactionId) { + _setRegister(tcp, 0, transactionId); +} + +void ModbusADU::setProtocolId(uint16_t protocolId) { + _setRegister(tcp, 2, protocolId); +} + +void ModbusADU::setLength(uint16_t length) { + if (length < 3 || length > 254) _setRegister(tcp, 4, 0); + else _setRegister(tcp, 4, length); +} + +void ModbusADU::setUnitId(uint8_t unitId) { + tcp[6] = unitId; +} + +void ModbusADU::setFunctionCode(uint8_t functionCode) { + pdu[0] = functionCode; +} + +void ModbusADU::setDataRegister(uint8_t index, uint16_t value) { + _setRegister(data, index, value); +} + + + +void ModbusADU::setRtuLen(uint16_t rtuLen) { + setLength(rtuLen - 2); +} + +void ModbusADU::setTcpLen(uint16_t tcpLen) { + setLength(tcpLen - 6); +} + +void ModbusADU::setPduLen(uint16_t pduLen) { + setLength(pduLen + 1); +} + +void ModbusADU::setDataLen(uint16_t dataLen) { + setLength(dataLen + 2); +} + + + +uint16_t ModbusADU::getTransactionId() { + return _getRegister(tcp, 0); +} + +uint16_t ModbusADU::getProtocolId() { + return _getRegister(tcp, 2); +} + +uint16_t ModbusADU::getLength() { + uint16_t length = _getRegister(tcp, 4); + if (length < 3 || length > 254) return 0; + else return length; +} + +uint8_t ModbusADU::getUnitId() { + return tcp[6]; +} + +uint8_t ModbusADU::getFunctionCode() { + return pdu[0]; +} + +uint16_t ModbusADU::getDataRegister(uint8_t index) { + return _getRegister(data, index); +} + + + +uint16_t ModbusADU::getRtuLen() { + uint16_t len = getLength(); + if (len == 0) return 0; + else return len + 2; +} + +uint16_t ModbusADU::getTcpLen() { + uint16_t len = getLength(); + if (len == 0) return 0; + else return len + 6; +} + +uint16_t ModbusADU::getPduLen() { + uint16_t len = getLength(); + if (len == 0) return 0; + else return len - 1; +} + +uint16_t ModbusADU::getDataLen() { + uint16_t len = getLength(); + if (len == 0) return 0; + else return len - 2; +} + + + +void ModbusADU::updateCrc() { + uint16_t len = getLength(); + uint16_t crc = _calculateCrc(len); + rtu[len] = lowByte(crc); + rtu[len + 1] = highByte(crc); +} + +bool ModbusADU::crcGood() { + uint16_t len = getLength(); + uint16_t aduCrc = rtu[len] | (rtu[len + 1] << 8); + uint16_t calculatedCrc = _calculateCrc(len); + if (aduCrc == calculatedCrc) return true; + else return false; +} + + + +void ModbusADU::prepareExceptionResponse(uint8_t exceptionCode) { + pdu[0] |= 0x80; + pdu[1] = exceptionCode; + setPduLen(2); +} + + + +void ModbusADU::_setRegister(uint8_t *buf, uint16_t index, uint16_t value) { + buf[index] = highByte(value); + buf[index + 1] = lowByte(value); +} + +uint16_t ModbusADU::_getRegister(uint8_t *buf, uint16_t index) { + return (buf[index] << 8) | buf[index + 1]; +} + +uint16_t ModbusADU::_calculateCrc(uint16_t len) { + uint16_t value = 0xFFFF; + for (uint16_t i = 0; i < len; i++) { + value ^= (uint16_t)rtu[i]; + for (uint8_t j = 0; j < 8; j++) { + bool lsb = value & 1; + value >>= 1; + if (lsb == true) value ^= 0xA001; + } + } + return value; +} + + + +uint16_t div8RndUp(uint16_t value) { + return (value + 7) >> 3; +} + +ModbusRTUComm::ModbusRTUComm(Stream& serial, int8_t dePin, int8_t rePin) : _serial(serial) { + _dePin = dePin; + _rePin = rePin; +} + +void ModbusRTUComm::begin(unsigned long baud, uint32_t config) { + unsigned long bitsPerChar; + switch (config) { + case SERIAL_8E2: + case SERIAL_8O2: + bitsPerChar = 12; + break; + case SERIAL_8N2: + case SERIAL_8E1: + case SERIAL_8O1: + bitsPerChar = 11; + break; + case SERIAL_8N1: + default: + bitsPerChar = 10; + break; + } + if (baud <= 19200) { + _charTimeout = (bitsPerChar * 2500000) / baud; + _frameTimeout = (bitsPerChar * 4500000) / baud; + } + else { + _charTimeout = (bitsPerChar * 1000000) / baud + 750; + _frameTimeout = (bitsPerChar * 1000000) / baud + 1750; + } + #if defined(ARDUINO_UNOR4_MINIMA) || defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_GIGA) || (defined(ARDUINO_NANO_RP2040_CONNECT) && defined(ARDUINO_ARCH_MBED)) + _postDelay = ((bitsPerChar * 1000000) / baud) + 2; + #endif + if (_dePin >= 0) { + pinMode(_dePin, OUTPUT); + digitalWrite(_dePin, LOW); + } + if (_rePin >= 0) { + pinMode(_rePin, OUTPUT); + digitalWrite(_rePin, LOW); + } + clearRxBuffer(); +} + +void ModbusRTUComm::setTimeout(unsigned long timeout) { + _readTimeout = timeout; +} + +ModbusRTUCommError ModbusRTUComm::readAdu(ModbusADU& adu) { + adu.setRtuLen(0); + unsigned long startMillis = millis(); + while (!_serial.available()) { + if (millis() - startMillis >= _readTimeout) return MODBUS_RTU_COMM_TIMEOUT; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (_serial.available()) { + startMicros = micros(); + adu.rtu[len] = _serial.read(); + len++; + } + } while (micros() - startMicros <= _charTimeout && len < 256); + adu.setRtuLen(len); + while (micros() - startMicros < _frameTimeout); + if (_serial.available()) { + adu.setRtuLen(0); + return MODBUS_RTU_COMM_FRAME_ERROR; + } + if (!adu.crcGood()) { + adu.setRtuLen(0); + return MODBUS_RTU_COMM_CRC_ERROR; + } + return MODBUS_RTU_COMM_SUCCESS; +} + +void ModbusRTUComm::writeAdu(ModbusADU& adu) { + adu.updateCrc(); + if (_dePin >= 0) digitalWrite(_dePin, HIGH); + if (_rePin >= 0) digitalWrite(_rePin, HIGH); + _serial.write(adu.rtu, adu.getRtuLen()); + _serial.flush(); + delayMicroseconds(_postDelay); + if (_dePin >= 0) digitalWrite(_dePin, LOW); + if (_rePin >= 0) digitalWrite(_rePin, LOW); +} + +void ModbusRTUComm::clearRxBuffer() { + unsigned long startMicros = micros(); + do { + if (_serial.available() > 0) { + startMicros = micros(); + _serial.read(); + } + } while (micros() - startMicros < _frameTimeout); +} + +ModbusRTUMaster::ModbusRTUMaster(Stream& serial, int8_t dePin, int8_t rePin) : _rtuComm(serial, dePin, rePin) { + _rtuComm.setTimeout(500); +} + +void ModbusRTUMaster::setTimeout(unsigned long timeout) { + _rtuComm.setTimeout(timeout); +} + +void ModbusRTUMaster::begin(unsigned long baud, uint32_t config) { + _rtuComm.begin(baud, config); +} + + + +ModbusRTUMasterError ModbusRTUMaster::readCoils(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity) { + return _readValues(id, 1, startAddress, buf, quantity); +} +ModbusRTUMasterError ModbusRTUMaster::readDiscreteInputs(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity) { + return _readValues(id, 2, startAddress, buf, quantity); +} + +ModbusRTUMasterError ModbusRTUMaster::readHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { + return _readValues(id, 3, startAddress, buf, quantity); +} + +ModbusRTUMasterError ModbusRTUMaster::readInputRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { + return _readValues(id, 4, startAddress, buf, quantity); +} + + + +ModbusRTUMasterError ModbusRTUMaster::writeSingleCoil(uint8_t id, uint16_t address, bool value) { + return _writeSingleValue(id, 5, address, ((value) ? 0xFF00 : 0x0000)); +} + +ModbusRTUMasterError ModbusRTUMaster::writeSingleHoldingRegister(uint8_t id, uint16_t address, uint16_t value) { + return _writeSingleValue(id, 6, address, value); +} + + + +ModbusRTUMasterError ModbusRTUMaster::writeMultipleCoils(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity) { + const uint8_t functionCode = 15; + if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; + if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; + if (quantity == 0 || quantity > 1968) return MODBUS_RTU_MASTER_INVALID_QUANTITY; + ModbusADU adu; + uint16_t byteCount = div8RndUp(quantity); + adu.setUnitId(id); + adu.setFunctionCode(functionCode); + adu.setDataRegister(0, startAddress); + adu.setDataRegister(2, quantity); + adu.data[4] = byteCount; + for (uint16_t i = 0; i < quantity; i++) { + bitWrite(adu.data[5 + (i >> 3)], i & 7, buf[i]); + } + for (uint16_t i = quantity; i < (byteCount * 8); i++) { + bitClear(adu.data[5 + (i >> 3)], i & 7); + } + adu.setDataLen(5 + byteCount); + _rtuComm.writeAdu(adu); + if (id == 0) return MODBUS_RTU_MASTER_SUCCESS; + ModbusRTUCommError commError = _rtuComm.readAdu(adu); + if (commError) return _translateCommError(commError); + if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; + if (adu.getFunctionCode() == (functionCode + 0x80)) { + _exceptionResponse = adu.data[0]; + return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; + } + if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; + if (adu.getDataLen() != 4) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; + if (adu.getDataRegister(0) != startAddress) return MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS; + if (adu.getDataRegister(2) != quantity) return MODBUS_RTU_MASTER_UNEXPECTED_QUANTITY; + return MODBUS_RTU_MASTER_SUCCESS; +} + +ModbusRTUMasterError ModbusRTUMaster::writeMultipleHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { + uint8_t functionCode = 16; + if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; + if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; + if (quantity == 0 || quantity > 123) return MODBUS_RTU_MASTER_INVALID_QUANTITY; + uint16_t byteCount = quantity * 2; + ModbusADU adu; + adu.setUnitId(id); + adu.setFunctionCode(functionCode); + adu.setDataRegister(0, startAddress); + adu.setDataRegister(2, quantity); + adu.data[4] = byteCount; + for (uint16_t i = 0; i < quantity; i++) { + adu.setDataRegister(5 + (i * 2), buf[i]); + } + adu.setDataLen(5 + byteCount); + _rtuComm.writeAdu(adu); + if (id == 0) return MODBUS_RTU_MASTER_SUCCESS; + ModbusRTUCommError commError = _rtuComm.readAdu(adu); + if (commError) return _translateCommError(commError); + if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; + if (adu.getFunctionCode() == (functionCode + 0x80)) { + _exceptionResponse = adu.data[0]; + return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; + } + if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; + if (adu.getDataLen() != 4) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; + if (adu.getDataRegister(0) != startAddress) return MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS; + if (adu.getDataRegister(2) != quantity) return MODBUS_RTU_MASTER_UNEXPECTED_QUANTITY; + return MODBUS_RTU_MASTER_SUCCESS; +} + + + +uint8_t ModbusRTUMaster::getExceptionResponse() { + return _exceptionResponse; +} + + + +ModbusRTUMasterError ModbusRTUMaster::_readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, bool buf[], uint16_t quantity) { + if (id < 1 || id > 247) return MODBUS_RTU_MASTER_INVALID_ID; + if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; + if (quantity == 0 || quantity > 2000) return MODBUS_RTU_MASTER_INVALID_QUANTITY; + ModbusADU adu; + adu.setUnitId(id); + adu.setFunctionCode(functionCode); + adu.setDataRegister(0, startAddress); + adu.setDataRegister(2, quantity); + adu.setDataLen(4); + _rtuComm.writeAdu(adu); + ModbusRTUCommError commError = _rtuComm.readAdu(adu); + if (commError) return _translateCommError(commError); + if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; + if (adu.getFunctionCode() == (functionCode + 0x80)) { + _exceptionResponse = adu.data[0]; + return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; + } + if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; + uint16_t byteCount = div8RndUp(quantity); + if (adu.getDataLen() != (1 + byteCount)) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; + if (adu.data[0] != byteCount) return MODBUS_RTU_MASTER_UNEXPECTED_BYTE_COUNT; + for (uint16_t i = 0; i < quantity; i++) { + buf[i] = bitRead(adu.data[1 + (i >> 3)], i & 7); + } + return MODBUS_RTU_MASTER_SUCCESS; +} + +ModbusRTUMasterError ModbusRTUMaster::_readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { + if (id < 1 || id > 247) return MODBUS_RTU_MASTER_INVALID_ID; + if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; + if (quantity == 0 || quantity > 125) return MODBUS_RTU_MASTER_INVALID_QUANTITY; + ModbusADU adu; + adu.setUnitId(id); + adu.setFunctionCode(functionCode); + adu.setDataRegister(0, startAddress); + adu.setDataRegister(2, quantity); + adu.setDataLen(4); + _rtuComm.writeAdu(adu); + ModbusRTUCommError commError = _rtuComm.readAdu(adu); + if (commError) return _translateCommError(commError); + if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; + if (adu.getFunctionCode() == (functionCode + 0x80)) { + _exceptionResponse = adu.data[0]; + return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; + } + if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; + uint16_t byteCount = quantity * 2; + if (adu.getDataLen() != (1 + byteCount)) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; + if (adu.data[0] != byteCount) return MODBUS_RTU_MASTER_UNEXPECTED_BYTE_COUNT; + for (uint16_t i = 0; i < quantity; i++) { + buf[i] = adu.getDataRegister(1 + (i * 2)); + } + return MODBUS_RTU_MASTER_SUCCESS; +} + +ModbusRTUMasterError ModbusRTUMaster::_writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value) { + if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; + ModbusADU adu; + adu.setUnitId(id); + adu.setFunctionCode(functionCode); + adu.setDataRegister(0, address); + adu.setDataRegister(2, value); + adu.setDataLen(4); + _rtuComm.writeAdu(adu); + if (id == 0) return MODBUS_RTU_MASTER_SUCCESS; + ModbusRTUCommError commError = _rtuComm.readAdu(adu); + if (commError) return _translateCommError(commError); + if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; + if (adu.getFunctionCode() == (functionCode + 0x80)) { + _exceptionResponse = adu.data[0]; + return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; + } + if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; + if (adu.getDataLen() != 4) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; + if (adu.getDataRegister(0) != address) return MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS; + if (adu.getDataRegister(2) != value) return MODBUS_RTU_MASTER_UNEXPECTED_VALUE; + return MODBUS_RTU_MASTER_SUCCESS; +} + + + +ModbusRTUMasterError ModbusRTUMaster::_translateCommError(ModbusRTUCommError commError) { + switch (commError) { + case MODBUS_RTU_COMM_SUCCESS: + return MODBUS_RTU_MASTER_SUCCESS; + case MODBUS_RTU_COMM_TIMEOUT: + return MODBUS_RTU_MASTER_RESPONSE_TIMEOUT; + case MODBUS_RTU_COMM_FRAME_ERROR: + return MODBUS_RTU_MASTER_FRAME_ERROR; + case MODBUS_RTU_COMM_CRC_ERROR: + return MODBUS_RTU_MASTER_CRC_ERROR; + default: + return MODBUS_RTU_MASTER_UNKNOWN_COMM_ERROR; + } +} /************************************************************ * Modbus implementation diff --git a/IO_Modbus.h b/IO_Modbus.h index 4933eeb6..be5b65bc 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -52,7 +52,129 @@ #define IO_MODBUS_H #include "IODevice.h" -#include "ModbusRTUMaster.h" +class ModbusADU { + public: + uint8_t *rtu = _adu + 6; + uint8_t *tcp = _adu; + uint8_t *pdu = _adu + 7; + uint8_t *data = _adu + 8; + + 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(); + uint16_t getLength(); + 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 updateCrc(); + bool crcGood(); + + void prepareExceptionResponse(uint8_t exceptionCode); + + private: + uint8_t _adu[262]; + void _setRegister(uint8_t *buf, uint16_t index, uint16_t value); + uint16_t _getRegister(uint8_t *buf, uint16_t index); + uint16_t _calculateCrc(uint16_t len); + +}; + +uint16_t div8RndUp(uint16_t value); + +enum ModbusRTUCommError : uint8_t { + MODBUS_RTU_COMM_SUCCESS = 0, + MODBUS_RTU_COMM_TIMEOUT = 1, + MODBUS_RTU_COMM_FRAME_ERROR = 2, + MODBUS_RTU_COMM_CRC_ERROR = 3 +}; + +class ModbusRTUComm { + public: + ModbusRTUComm(Stream& serial, int8_t dePin = -1, int8_t rePin = -1); + void begin(unsigned long baud, uint32_t config = SERIAL_8N1); + void setTimeout(unsigned long timeout); + ModbusRTUCommError readAdu(ModbusADU& adu); + void writeAdu(ModbusADU& adu); + void clearRxBuffer(); + + private: + Stream& _serial; + int8_t _dePin; + int8_t _rePin; + unsigned long _charTimeout; + unsigned long _frameTimeout; + unsigned long _postDelay = 0; + unsigned long _readTimeout = 0; +}; + +enum ModbusRTUMasterError : uint8_t { + MODBUS_RTU_MASTER_SUCCESS = 0, + MODBUS_RTU_MASTER_INVALID_ID = 1, + MODBUS_RTU_MASTER_INVALID_BUFFER = 2, + MODBUS_RTU_MASTER_INVALID_QUANTITY = 3, + MODBUS_RTU_MASTER_RESPONSE_TIMEOUT = 4, + MODBUS_RTU_MASTER_FRAME_ERROR = 5, + MODBUS_RTU_MASTER_CRC_ERROR = 6, + MODBUS_RTU_MASTER_UNKNOWN_COMM_ERROR = 7, + MODBUS_RTU_MASTER_UNEXPECTED_ID = 8, + MODBUS_RTU_MASTER_EXCEPTION_RESPONSE = 9, + MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE = 10, + MODBUS_RTU_MASTER_UNEXPECTED_LENGTH = 11, + MODBUS_RTU_MASTER_UNEXPECTED_BYTE_COUNT = 12, + MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS = 13, + MODBUS_RTU_MASTER_UNEXPECTED_VALUE = 14, + MODBUS_RTU_MASTER_UNEXPECTED_QUANTITY = 15 +}; + +class ModbusRTUMaster { + public: + ModbusRTUMaster(Stream& serial, int8_t dePin = -1, int8_t rePin = -1); + void setTimeout(unsigned long timeout); + void begin(unsigned long baud, uint32_t config = SERIAL_8N1); + + ModbusRTUMasterError readCoils(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity); + ModbusRTUMasterError readDiscreteInputs(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity); + ModbusRTUMasterError readHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); + ModbusRTUMasterError readInputRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); + + ModbusRTUMasterError writeSingleCoil(uint8_t id, uint16_t address, bool value); + ModbusRTUMasterError writeSingleHoldingRegister(uint8_t id, uint16_t address, uint16_t value); + ModbusRTUMasterError writeMultipleCoils(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity); + ModbusRTUMasterError writeMultipleHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); + + uint8_t getExceptionResponse(); + + private: + ModbusRTUComm _rtuComm; + uint8_t _exceptionResponse = 0; + + ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, bool buf[], uint16_t quantity); + ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity); + ModbusRTUMasterError _writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value); + + ModbusRTUMasterError _translateCommError(ModbusRTUCommError commError); + +}; + + + /********************************************************************** * Modbusnode class * diff --git a/ModbusADU.cpp b/ModbusADU.cpp deleted file mode 100644 index b7752b28..00000000 --- a/ModbusADU.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#include "ModbusADU.h" - -void ModbusADU::setTransactionId(uint16_t transactionId) { - _setRegister(tcp, 0, transactionId); -} - -void ModbusADU::setProtocolId(uint16_t protocolId) { - _setRegister(tcp, 2, protocolId); -} - -void ModbusADU::setLength(uint16_t length) { - if (length < 3 || length > 254) _setRegister(tcp, 4, 0); - else _setRegister(tcp, 4, length); -} - -void ModbusADU::setUnitId(uint8_t unitId) { - tcp[6] = unitId; -} - -void ModbusADU::setFunctionCode(uint8_t functionCode) { - pdu[0] = functionCode; -} - -void ModbusADU::setDataRegister(uint8_t index, uint16_t value) { - _setRegister(data, index, value); -} - - - -void ModbusADU::setRtuLen(uint16_t rtuLen) { - setLength(rtuLen - 2); -} - -void ModbusADU::setTcpLen(uint16_t tcpLen) { - setLength(tcpLen - 6); -} - -void ModbusADU::setPduLen(uint16_t pduLen) { - setLength(pduLen + 1); -} - -void ModbusADU::setDataLen(uint16_t dataLen) { - setLength(dataLen + 2); -} - - - -uint16_t ModbusADU::getTransactionId() { - return _getRegister(tcp, 0); -} - -uint16_t ModbusADU::getProtocolId() { - return _getRegister(tcp, 2); -} - -uint16_t ModbusADU::getLength() { - uint16_t length = _getRegister(tcp, 4); - if (length < 3 || length > 254) return 0; - else return length; -} - -uint8_t ModbusADU::getUnitId() { - return tcp[6]; -} - -uint8_t ModbusADU::getFunctionCode() { - return pdu[0]; -} - -uint16_t ModbusADU::getDataRegister(uint8_t index) { - return _getRegister(data, index); -} - - - -uint16_t ModbusADU::getRtuLen() { - uint16_t len = getLength(); - if (len == 0) return 0; - else return len + 2; -} - -uint16_t ModbusADU::getTcpLen() { - uint16_t len = getLength(); - if (len == 0) return 0; - else return len + 6; -} - -uint16_t ModbusADU::getPduLen() { - uint16_t len = getLength(); - if (len == 0) return 0; - else return len - 1; -} - -uint16_t ModbusADU::getDataLen() { - uint16_t len = getLength(); - if (len == 0) return 0; - else return len - 2; -} - - - -void ModbusADU::updateCrc() { - uint16_t len = getLength(); - uint16_t crc = _calculateCrc(len); - rtu[len] = lowByte(crc); - rtu[len + 1] = highByte(crc); -} - -bool ModbusADU::crcGood() { - uint16_t len = getLength(); - uint16_t aduCrc = rtu[len] | (rtu[len + 1] << 8); - uint16_t calculatedCrc = _calculateCrc(len); - if (aduCrc == calculatedCrc) return true; - else return false; -} - - - -void ModbusADU::prepareExceptionResponse(uint8_t exceptionCode) { - pdu[0] |= 0x80; - pdu[1] = exceptionCode; - setPduLen(2); -} - - - -void ModbusADU::_setRegister(uint8_t *buf, uint16_t index, uint16_t value) { - buf[index] = highByte(value); - buf[index + 1] = lowByte(value); -} - -uint16_t ModbusADU::_getRegister(uint8_t *buf, uint16_t index) { - return (buf[index] << 8) | buf[index + 1]; -} - -uint16_t ModbusADU::_calculateCrc(uint16_t len) { - uint16_t value = 0xFFFF; - for (uint16_t i = 0; i < len; i++) { - value ^= (uint16_t)rtu[i]; - for (uint8_t j = 0; j < 8; j++) { - bool lsb = value & 1; - value >>= 1; - if (lsb == true) value ^= 0xA001; - } - } - return value; -} - - - -uint16_t div8RndUp(uint16_t value) { - return (value + 7) >> 3; -} \ No newline at end of file diff --git a/ModbusADU.h b/ModbusADU.h deleted file mode 100644 index 2aa72d96..00000000 --- a/ModbusADU.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef ModbusADU_h -#define ModbusADU_h - -#include "Arduino.h" - -class ModbusADU { - public: - uint8_t *rtu = _adu + 6; - uint8_t *tcp = _adu; - uint8_t *pdu = _adu + 7; - uint8_t *data = _adu + 8; - - 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(); - uint16_t getLength(); - 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 updateCrc(); - bool crcGood(); - - void prepareExceptionResponse(uint8_t exceptionCode); - - private: - uint8_t _adu[262]; - void _setRegister(uint8_t *buf, uint16_t index, uint16_t value); - uint16_t _getRegister(uint8_t *buf, uint16_t index); - uint16_t _calculateCrc(uint16_t len); - -}; - -uint16_t div8RndUp(uint16_t value); - -#endif \ No newline at end of file diff --git a/ModbusRTUComm.cpp b/ModbusRTUComm.cpp deleted file mode 100644 index 2abbe960..00000000 --- a/ModbusRTUComm.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "ModbusRTUComm.h" - -ModbusRTUComm::ModbusRTUComm(Stream& serial, int8_t dePin, int8_t rePin) : _serial(serial) { - _dePin = dePin; - _rePin = rePin; -} - -void ModbusRTUComm::begin(unsigned long baud, uint32_t config) { - unsigned long bitsPerChar; - switch (config) { - case SERIAL_8E2: - case SERIAL_8O2: - bitsPerChar = 12; - break; - case SERIAL_8N2: - case SERIAL_8E1: - case SERIAL_8O1: - bitsPerChar = 11; - break; - case SERIAL_8N1: - default: - bitsPerChar = 10; - break; - } - if (baud <= 19200) { - _charTimeout = (bitsPerChar * 2500000) / baud; - _frameTimeout = (bitsPerChar * 4500000) / baud; - } - else { - _charTimeout = (bitsPerChar * 1000000) / baud + 750; - _frameTimeout = (bitsPerChar * 1000000) / baud + 1750; - } - #if defined(ARDUINO_UNOR4_MINIMA) || defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_GIGA) || (defined(ARDUINO_NANO_RP2040_CONNECT) && defined(ARDUINO_ARCH_MBED)) - _postDelay = ((bitsPerChar * 1000000) / baud) + 2; - #endif - if (_dePin >= 0) { - pinMode(_dePin, OUTPUT); - digitalWrite(_dePin, LOW); - } - if (_rePin >= 0) { - pinMode(_rePin, OUTPUT); - digitalWrite(_rePin, LOW); - } - clearRxBuffer(); -} - -void ModbusRTUComm::setTimeout(unsigned long timeout) { - _readTimeout = timeout; -} - -ModbusRTUCommError ModbusRTUComm::readAdu(ModbusADU& adu) { - adu.setRtuLen(0); - unsigned long startMillis = millis(); - while (!_serial.available()) { - if (millis() - startMillis >= _readTimeout) return MODBUS_RTU_COMM_TIMEOUT; - } - uint16_t len = 0; - unsigned long startMicros = micros(); - do { - if (_serial.available()) { - startMicros = micros(); - adu.rtu[len] = _serial.read(); - len++; - } - } while (micros() - startMicros <= _charTimeout && len < 256); - adu.setRtuLen(len); - while (micros() - startMicros < _frameTimeout); - if (_serial.available()) { - adu.setRtuLen(0); - return MODBUS_RTU_COMM_FRAME_ERROR; - } - if (!adu.crcGood()) { - adu.setRtuLen(0); - return MODBUS_RTU_COMM_CRC_ERROR; - } - return MODBUS_RTU_COMM_SUCCESS; -} - -void ModbusRTUComm::writeAdu(ModbusADU& adu) { - adu.updateCrc(); - if (_dePin >= 0) digitalWrite(_dePin, HIGH); - if (_rePin >= 0) digitalWrite(_rePin, HIGH); - _serial.write(adu.rtu, adu.getRtuLen()); - _serial.flush(); - delayMicroseconds(_postDelay); - if (_dePin >= 0) digitalWrite(_dePin, LOW); - if (_rePin >= 0) digitalWrite(_rePin, LOW); -} - -void ModbusRTUComm::clearRxBuffer() { - unsigned long startMicros = micros(); - do { - if (_serial.available() > 0) { - startMicros = micros(); - _serial.read(); - } - } while (micros() - startMicros < _frameTimeout); -} diff --git a/ModbusRTUComm.h b/ModbusRTUComm.h deleted file mode 100644 index cae72341..00000000 --- a/ModbusRTUComm.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef ModbusRTUComm_h -#define ModbusRTUComm_h - -#include "Arduino.h" -#include "ModbusADU.h" - -enum ModbusRTUCommError : uint8_t { - MODBUS_RTU_COMM_SUCCESS = 0, - MODBUS_RTU_COMM_TIMEOUT = 1, - MODBUS_RTU_COMM_FRAME_ERROR = 2, - MODBUS_RTU_COMM_CRC_ERROR = 3 -}; - -class ModbusRTUComm { - public: - ModbusRTUComm(Stream& serial, int8_t dePin = -1, int8_t rePin = -1); - void begin(unsigned long baud, uint32_t config = SERIAL_8N1); - void setTimeout(unsigned long timeout); - ModbusRTUCommError readAdu(ModbusADU& adu); - void writeAdu(ModbusADU& adu); - void clearRxBuffer(); - - private: - Stream& _serial; - int8_t _dePin; - int8_t _rePin; - unsigned long _charTimeout; - unsigned long _frameTimeout; - unsigned long _postDelay = 0; - unsigned long _readTimeout = 0; -}; - -#endif \ No newline at end of file diff --git a/ModbusRTUMaster.cpp b/ModbusRTUMaster.cpp deleted file mode 100644 index 897ce5ae..00000000 --- a/ModbusRTUMaster.cpp +++ /dev/null @@ -1,216 +0,0 @@ -#include "ModbusRTUMaster.h" - -ModbusRTUMaster::ModbusRTUMaster(Stream& serial, int8_t dePin, int8_t rePin) : _rtuComm(serial, dePin, rePin) { - _rtuComm.setTimeout(500); -} - -void ModbusRTUMaster::setTimeout(unsigned long timeout) { - _rtuComm.setTimeout(timeout); -} - -void ModbusRTUMaster::begin(unsigned long baud, uint32_t config) { - _rtuComm.begin(baud, config); -} - - - -ModbusRTUMasterError ModbusRTUMaster::readCoils(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity) { - return _readValues(id, 1, startAddress, buf, quantity); -} - -ModbusRTUMasterError ModbusRTUMaster::readDiscreteInputs(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity) { - return _readValues(id, 2, startAddress, buf, quantity); -} - -ModbusRTUMasterError ModbusRTUMaster::readHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { - return _readValues(id, 3, startAddress, buf, quantity); -} - -ModbusRTUMasterError ModbusRTUMaster::readInputRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { - return _readValues(id, 4, startAddress, buf, quantity); -} - - - -ModbusRTUMasterError ModbusRTUMaster::writeSingleCoil(uint8_t id, uint16_t address, bool value) { - return _writeSingleValue(id, 5, address, ((value) ? 0xFF00 : 0x0000)); -} - -ModbusRTUMasterError ModbusRTUMaster::writeSingleHoldingRegister(uint8_t id, uint16_t address, uint16_t value) { - return _writeSingleValue(id, 6, address, value); -} - - - -ModbusRTUMasterError ModbusRTUMaster::writeMultipleCoils(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity) { - const uint8_t functionCode = 15; - if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; - if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; - if (quantity == 0 || quantity > 1968) return MODBUS_RTU_MASTER_INVALID_QUANTITY; - ModbusADU adu; - uint16_t byteCount = div8RndUp(quantity); - adu.setUnitId(id); - adu.setFunctionCode(functionCode); - adu.setDataRegister(0, startAddress); - adu.setDataRegister(2, quantity); - adu.data[4] = byteCount; - for (uint16_t i = 0; i < quantity; i++) { - bitWrite(adu.data[5 + (i >> 3)], i & 7, buf[i]); - } - for (uint16_t i = quantity; i < (byteCount * 8); i++) { - bitClear(adu.data[5 + (i >> 3)], i & 7); - } - adu.setDataLen(5 + byteCount); - _rtuComm.writeAdu(adu); - if (id == 0) return MODBUS_RTU_MASTER_SUCCESS; - ModbusRTUCommError commError = _rtuComm.readAdu(adu); - if (commError) return _translateCommError(commError); - if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; - if (adu.getFunctionCode() == (functionCode + 0x80)) { - _exceptionResponse = adu.data[0]; - return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; - } - if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; - if (adu.getDataLen() != 4) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; - if (adu.getDataRegister(0) != startAddress) return MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS; - if (adu.getDataRegister(2) != quantity) return MODBUS_RTU_MASTER_UNEXPECTED_QUANTITY; - return MODBUS_RTU_MASTER_SUCCESS; -} - -ModbusRTUMasterError ModbusRTUMaster::writeMultipleHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { - uint8_t functionCode = 16; - if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; - if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; - if (quantity == 0 || quantity > 123) return MODBUS_RTU_MASTER_INVALID_QUANTITY; - uint16_t byteCount = quantity * 2; - ModbusADU adu; - adu.setUnitId(id); - adu.setFunctionCode(functionCode); - adu.setDataRegister(0, startAddress); - adu.setDataRegister(2, quantity); - adu.data[4] = byteCount; - for (uint16_t i = 0; i < quantity; i++) { - adu.setDataRegister(5 + (i * 2), buf[i]); - } - adu.setDataLen(5 + byteCount); - _rtuComm.writeAdu(adu); - if (id == 0) return MODBUS_RTU_MASTER_SUCCESS; - ModbusRTUCommError commError = _rtuComm.readAdu(adu); - if (commError) return _translateCommError(commError); - if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; - if (adu.getFunctionCode() == (functionCode + 0x80)) { - _exceptionResponse = adu.data[0]; - return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; - } - if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; - if (adu.getDataLen() != 4) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; - if (adu.getDataRegister(0) != startAddress) return MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS; - if (adu.getDataRegister(2) != quantity) return MODBUS_RTU_MASTER_UNEXPECTED_QUANTITY; - return MODBUS_RTU_MASTER_SUCCESS; -} - - - -uint8_t ModbusRTUMaster::getExceptionResponse() { - return _exceptionResponse; -} - - - -ModbusRTUMasterError ModbusRTUMaster::_readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, bool buf[], uint16_t quantity) { - if (id < 1 || id > 247) return MODBUS_RTU_MASTER_INVALID_ID; - if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; - if (quantity == 0 || quantity > 2000) return MODBUS_RTU_MASTER_INVALID_QUANTITY; - ModbusADU adu; - adu.setUnitId(id); - adu.setFunctionCode(functionCode); - adu.setDataRegister(0, startAddress); - adu.setDataRegister(2, quantity); - adu.setDataLen(4); - _rtuComm.writeAdu(adu); - ModbusRTUCommError commError = _rtuComm.readAdu(adu); - if (commError) return _translateCommError(commError); - if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; - if (adu.getFunctionCode() == (functionCode + 0x80)) { - _exceptionResponse = adu.data[0]; - return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; - } - if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; - uint16_t byteCount = div8RndUp(quantity); - if (adu.getDataLen() != (1 + byteCount)) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; - if (adu.data[0] != byteCount) return MODBUS_RTU_MASTER_UNEXPECTED_BYTE_COUNT; - for (uint16_t i = 0; i < quantity; i++) { - buf[i] = bitRead(adu.data[1 + (i >> 3)], i & 7); - } - return MODBUS_RTU_MASTER_SUCCESS; -} - -ModbusRTUMasterError ModbusRTUMaster::_readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { - if (id < 1 || id > 247) return MODBUS_RTU_MASTER_INVALID_ID; - if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; - if (quantity == 0 || quantity > 125) return MODBUS_RTU_MASTER_INVALID_QUANTITY; - ModbusADU adu; - adu.setUnitId(id); - adu.setFunctionCode(functionCode); - adu.setDataRegister(0, startAddress); - adu.setDataRegister(2, quantity); - adu.setDataLen(4); - _rtuComm.writeAdu(adu); - ModbusRTUCommError commError = _rtuComm.readAdu(adu); - if (commError) return _translateCommError(commError); - if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; - if (adu.getFunctionCode() == (functionCode + 0x80)) { - _exceptionResponse = adu.data[0]; - return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; - } - if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; - uint16_t byteCount = quantity * 2; - if (adu.getDataLen() != (1 + byteCount)) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; - if (adu.data[0] != byteCount) return MODBUS_RTU_MASTER_UNEXPECTED_BYTE_COUNT; - for (uint16_t i = 0; i < quantity; i++) { - buf[i] = adu.getDataRegister(1 + (i * 2)); - } - return MODBUS_RTU_MASTER_SUCCESS; -} - -ModbusRTUMasterError ModbusRTUMaster::_writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value) { - if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; - ModbusADU adu; - adu.setUnitId(id); - adu.setFunctionCode(functionCode); - adu.setDataRegister(0, address); - adu.setDataRegister(2, value); - adu.setDataLen(4); - _rtuComm.writeAdu(adu); - if (id == 0) return MODBUS_RTU_MASTER_SUCCESS; - ModbusRTUCommError commError = _rtuComm.readAdu(adu); - if (commError) return _translateCommError(commError); - if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; - if (adu.getFunctionCode() == (functionCode + 0x80)) { - _exceptionResponse = adu.data[0]; - return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; - } - if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; - if (adu.getDataLen() != 4) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; - if (adu.getDataRegister(0) != address) return MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS; - if (adu.getDataRegister(2) != value) return MODBUS_RTU_MASTER_UNEXPECTED_VALUE; - return MODBUS_RTU_MASTER_SUCCESS; -} - - - -ModbusRTUMasterError ModbusRTUMaster::_translateCommError(ModbusRTUCommError commError) { - switch (commError) { - case MODBUS_RTU_COMM_SUCCESS: - return MODBUS_RTU_MASTER_SUCCESS; - case MODBUS_RTU_COMM_TIMEOUT: - return MODBUS_RTU_MASTER_RESPONSE_TIMEOUT; - case MODBUS_RTU_COMM_FRAME_ERROR: - return MODBUS_RTU_MASTER_FRAME_ERROR; - case MODBUS_RTU_COMM_CRC_ERROR: - return MODBUS_RTU_MASTER_CRC_ERROR; - default: - return MODBUS_RTU_MASTER_UNKNOWN_COMM_ERROR; - } -} - diff --git a/ModbusRTUMaster.h b/ModbusRTUMaster.h deleted file mode 100644 index d9193c35..00000000 --- a/ModbusRTUMaster.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef ModbusRTUMaster_h -#define ModbusRTUMaster_h - -#include "Arduino.h" -#include "ModbusADU.h" -#include "ModbusRTUComm.h" - -enum ModbusRTUMasterError : uint8_t { - MODBUS_RTU_MASTER_SUCCESS = 0, - MODBUS_RTU_MASTER_INVALID_ID = 1, - MODBUS_RTU_MASTER_INVALID_BUFFER = 2, - MODBUS_RTU_MASTER_INVALID_QUANTITY = 3, - MODBUS_RTU_MASTER_RESPONSE_TIMEOUT = 4, - MODBUS_RTU_MASTER_FRAME_ERROR = 5, - MODBUS_RTU_MASTER_CRC_ERROR = 6, - MODBUS_RTU_MASTER_UNKNOWN_COMM_ERROR = 7, - MODBUS_RTU_MASTER_UNEXPECTED_ID = 8, - MODBUS_RTU_MASTER_EXCEPTION_RESPONSE = 9, - MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE = 10, - MODBUS_RTU_MASTER_UNEXPECTED_LENGTH = 11, - MODBUS_RTU_MASTER_UNEXPECTED_BYTE_COUNT = 12, - MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS = 13, - MODBUS_RTU_MASTER_UNEXPECTED_VALUE = 14, - MODBUS_RTU_MASTER_UNEXPECTED_QUANTITY = 15 -}; - -class ModbusRTUMaster { - public: - ModbusRTUMaster(Stream& serial, int8_t dePin = -1, int8_t rePin = -1); - void setTimeout(unsigned long timeout); - void begin(unsigned long baud, uint32_t config = SERIAL_8N1); - - ModbusRTUMasterError readCoils(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity); - ModbusRTUMasterError readDiscreteInputs(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity); - ModbusRTUMasterError readHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); - ModbusRTUMasterError readInputRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); - - ModbusRTUMasterError writeSingleCoil(uint8_t id, uint16_t address, bool value); - ModbusRTUMasterError writeSingleHoldingRegister(uint8_t id, uint16_t address, uint16_t value); - ModbusRTUMasterError writeMultipleCoils(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity); - ModbusRTUMasterError writeMultipleHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); - - uint8_t getExceptionResponse(); - - private: - ModbusRTUComm _rtuComm; - uint8_t _exceptionResponse = 0; - - ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, bool buf[], uint16_t quantity); - ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity); - ModbusRTUMasterError _writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value); - - ModbusRTUMasterError _translateCommError(ModbusRTUCommError commError); - -}; - -#endif From 56a68fd9afb6d5f04eb43b0d0e7d50fb60f2b59f Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sun, 24 Nov 2024 05:23:43 -0500 Subject: [PATCH 03/73] possible flaboo fix --- IO_Modbus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IO_Modbus.h b/IO_Modbus.h index be5b65bc..2e929f2a 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -238,7 +238,7 @@ class Modbusnode : public IODevice { void _write(VPIN vpin, int value) { // Update current state for this device, in preparation the bus transmission - uint16_t pin = vpin - _firstVpin - numCoils; + uint16_t pin = vpin - _firstVpin - numDiscreteInputs; if (pin < numCoils) { if (value) coils[pin] = value; From e01de49d36440004dfb7e4e1f94e66405bd5ccca Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sun, 24 Nov 2024 05:33:10 -0500 Subject: [PATCH 04/73] Diag message fix --- IO_Modbus.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 7404b12c..91891d11 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -522,8 +522,8 @@ void Modbus::_loop(unsigned long currentMicros) { // If we're between read/write cycles then don't do anything else. if (_currentMicros - _cycleStartTime < _cycleTime) return; // ... otherwise start processing the first node in the list - DIAG(F("Modbusnode: 138 _nodeListEnd:%d "), _nodeListEnd); - DIAG(F("Modbusnode: 139 _currentNode:%d "), _currentNode); + DIAG(F("Modbusnode: End _nodeListEnd:%d "), _nodeListEnd); + DIAG(F("Modbusnode: Cur _currentNode:%d "), _currentNode); _currentNode = _nodeListStart; DIAG(F("Modbusnode: 141 _currentNode:%d "), _currentNode); _cycleStartTime = _currentMicros; @@ -532,10 +532,10 @@ void Modbus::_loop(unsigned long currentMicros) { uint8_t error; error = modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, _currentNode->coils, _currentNode->getNumCoils()); - if (error != 0) DIAG(F("%02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); + if (error != 0) DIAG(F("Modbus: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); error = modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, _currentNode->discreteInputs, _currentNode->getNumDisInputs()); - if (error != 0) DIAG(F("%02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDisInputs(), errorStrings[error]); + if (error != 0) DIAG(F("Modbus: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDisInputs(), errorStrings[error]); } // Link to chain of CMRI bus instances From 406c0573353a9fd0a7be5d58c832460eea10f8fa Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sun, 24 Nov 2024 15:31:55 -0500 Subject: [PATCH 05/73] runs but does not work yet --- IO_Modbus.cpp | 16 +++++----------- IO_Modbus.h | 3 ++- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 91891d11..8abe4d8f 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -493,11 +493,11 @@ Modbus::Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_ _serial = &serial; _cycleTime = cycleTimeMS * 1000UL; // convert from milliseconds to microseconds. _transmitEnablePin = transmitEnablePin; - if (_transmitEnablePin != VPIN_NONE) { - pinMode(_transmitEnablePin, OUTPUT); - ArduinoPins::fastWriteDigital(_transmitEnablePin, 0); // transmitter initially off - } - ModbusRTUMaster modbusmaster(*_serial, _transmitEnablePin); + //if (_transmitEnablePin != VPIN_NONE) { + //pinMode(_transmitEnablePin, OUTPUT); + //ArduinoPins::fastWriteDigital(_transmitEnablePin, 0); // transmitter initially off + //} + // Add device to HAL device chain IODevice::addDevice(this); @@ -521,12 +521,6 @@ void Modbus::_loop(unsigned long currentMicros) { if (_currentNode == NULL) { // If we're between read/write cycles then don't do anything else. if (_currentMicros - _cycleStartTime < _cycleTime) return; - // ... otherwise start processing the first node in the list - DIAG(F("Modbusnode: End _nodeListEnd:%d "), _nodeListEnd); - DIAG(F("Modbusnode: Cur _currentNode:%d "), _currentNode); - _currentNode = _nodeListStart; - DIAG(F("Modbusnode: 141 _currentNode:%d "), _currentNode); - _cycleStartTime = _currentMicros; } if (_currentNode == NULL) return; diff --git a/IO_Modbus.h b/IO_Modbus.h index 2e929f2a..165225e2 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -322,8 +322,9 @@ class Modbus : public IODevice { const char* errorStrings[]; // Device-specific initialisation void _begin() override { + ModbusRTUMaster modbusmaster(*_serial, _transmitEnablePin); _serial->begin(_baud, SERIAL_8N1); - modbusmaster->begin(_baud); + modbusmaster.begin(_baud); #if defined(DIAG_IO) _display(); #endif From 6d2586f88ef975500d47a926f8b3735285e9cc54 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Mon, 25 Nov 2024 05:18:16 -0500 Subject: [PATCH 06/73] added analog, though still no positive test --- IO_Modbus.cpp | 15 ++++++++++++--- IO_Modbus.h | 40 +++++++++++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 8abe4d8f..888b470c 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -522,14 +522,21 @@ void Modbus::_loop(unsigned long currentMicros) { // If we're between read/write cycles then don't do anything else. if (_currentMicros - _cycleStartTime < _cycleTime) return; } + _cycleStartTime = _currentMicros; if (_currentNode == NULL) return; uint8_t error; + error = modbusmaster->writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); + if (error != 0) DIAG(F("ModbusHR: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); + error = modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, _currentNode->coils, _currentNode->getNumCoils()); - if (error != 0) DIAG(F("Modbus: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); + if (error != 0) DIAG(F("ModbusMC: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); error = modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, _currentNode->discreteInputs, _currentNode->getNumDisInputs()); - if (error != 0) DIAG(F("Modbus: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDisInputs(), errorStrings[error]); + if (error != 0) DIAG(F("ModbusDI: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDisInputs(), errorStrings[error]); + + error = modbusmaster->readInputRegisters(_currentNode->getNodeID(), 0, _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); + if (error != 0) DIAG(F("ModbusIR: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); } // Link to chain of CMRI bus instances @@ -541,13 +548,15 @@ Modbus *Modbus::_busList = NULL; ************************************************************/ // Constructor for Modbusnode object -Modbusnode::Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils, uint8_t numDiscreteInputs) { +Modbusnode::Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils, uint8_t numDiscreteInputs, uint8_t numHoldingRegisters, uint8_t numInputRegisters) { _firstVpin = firstVpin; _nPins = nPins; _busNo = busNo; _nodeID = nodeID; coils[numCoils]; discreteInputs[numDiscreteInputs]; + holdingRegisters[numHoldingRegisters]; + inputRegisters[numInputRegisters]; if ((unsigned int)_nPins < numDiscreteInputs + numCoils) DIAG(F("Modbusnode: bus:%d nodeID:%d WARNING number of Vpins does not cover all inputs and outputs"), _busNo, _nodeID); diff --git a/IO_Modbus.h b/IO_Modbus.h index 165225e2..7590e874 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -191,14 +191,18 @@ class Modbusnode : public IODevice { bool _initialised = false; uint8_t numCoils; uint8_t numDiscreteInputs; + uint8_t numHoldingRegisters; + uint8_t numInputRegisters; public: - static void create(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils=0, uint8_t numDiscreteInputs=0) { - if (checkNoOverlap(firstVpin, nPins)) new Modbusnode(firstVpin, nPins, busNo, nodeID, numCoils, numDiscreteInputs); + static void create(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils=0, uint8_t numDiscreteInputs=0, uint8_t numHoldingRegisters=0, uint8_t numInputRegisters=0) { + if (checkNoOverlap(firstVpin, nPins)) new Modbusnode(firstVpin, nPins, busNo, nodeID, numCoils, numDiscreteInputs, numHoldingRegisters, numInputRegisters); } - Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils=0, uint8_t numDiscreteInputs=0); + Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils=0, uint8_t numDiscreteInputs=0, uint8_t numHoldingRegisters=0, uint8_t numInputRegisters=0); bool *coils; bool *discreteInputs; + uint16_t *holdingRegisters; + uint16_t *inputRegisters; uint8_t getNodeID() { return _nodeID; @@ -209,7 +213,12 @@ class Modbusnode : public IODevice { uint8_t getNumDisInputs() { return numDiscreteInputs; } - + uint8_t getNumHoldingRegisters() { + return numHoldingRegisters; + } + uint8_t getNumInputRegisters() { + return numInputRegisters; + } Modbusnode *getNext() { return _next; } @@ -227,7 +236,7 @@ class Modbusnode : public IODevice { _initialised = false; } - int _read(VPIN vpin) { + int _read(VPIN vpin) override { // Return current state from this device uint16_t pin = vpin - _firstVpin; if (pin < numDiscreteInputs) { @@ -236,7 +245,13 @@ class Modbusnode : public IODevice { return 0; } - void _write(VPIN vpin, int value) { + int _readAnalogue(VPIN vpin) override { + // Return acquired data value, e.g. + int pin = vpin - _firstVpin; + return inputRegisters[pin]; + } + + void _write(VPIN vpin, int value) override { // Update current state for this device, in preparation the bus transmission uint16_t pin = vpin - _firstVpin - numDiscreteInputs; if (pin < numCoils) { @@ -247,6 +262,16 @@ class Modbusnode : public IODevice { } } + void writeAnalogue(VPIN vpin, int value) { + uint16_t pin = vpin - _firstVpin - numInputRegisters; + if (pin < numHoldingRegisters) { + if (value) + holdingRegisters[pin] = value; + else + holdingRegisters[pin]; + } + } + void saveIncomingData(uint8_t index, uint8_t data) { if (index < numDiscreteInputs) discreteInputs[index] = data; @@ -300,7 +325,7 @@ class Modbus : public IODevice { int16_t _transmitEnablePin = VPIN_NONE; Modbusnode *_nodeListStart = NULL, *_nodeListEnd = NULL; Modbusnode *_currentNode = NULL; - + uint16_t _receiveDataIndex = 0; // Index of next data byte to be received. Modbus *_nextBus = NULL; // Pointer to next bus instance in list. unsigned long _cycleStartTime = 0; @@ -319,6 +344,7 @@ class Modbus : public IODevice { } HardwareSerial *_serial; ModbusRTUMaster *modbusmaster; + const char* errorStrings[]; // Device-specific initialisation void _begin() override { From 75a00f776a65f59e81da7fe96c129fbb4b9dabda Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Mon, 25 Nov 2024 05:26:46 -0500 Subject: [PATCH 07/73] dereference errorStrings? --- IO_Modbus.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 888b470c..77034429 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -527,16 +527,16 @@ void Modbus::_loop(unsigned long currentMicros) { uint8_t error; error = modbusmaster->writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); - if (error != 0) DIAG(F("ModbusHR: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); + if (error != 0) DIAG(F("ModbusHR: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), &errorStrings[error]); error = modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, _currentNode->coils, _currentNode->getNumCoils()); - if (error != 0) DIAG(F("ModbusMC: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); + if (error != 0) DIAG(F("ModbusMC: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), &errorStrings[error]); error = modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, _currentNode->discreteInputs, _currentNode->getNumDisInputs()); - if (error != 0) DIAG(F("ModbusDI: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDisInputs(), errorStrings[error]); + if (error != 0) DIAG(F("ModbusDI: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDisInputs(), &errorStrings[error]); error = modbusmaster->readInputRegisters(_currentNode->getNodeID(), 0, _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); - if (error != 0) DIAG(F("ModbusIR: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); + if (error != 0) DIAG(F("ModbusIR: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), &errorStrings[error]); } // Link to chain of CMRI bus instances From 68a44037c52338cecddcd02abea1387ca3d63163 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Mon, 25 Nov 2024 05:28:49 -0500 Subject: [PATCH 08/73] move errorStrings --- IO_Modbus.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 77034429..259429f0 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -505,7 +505,6 @@ Modbus::Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_ // Add bus to CMRIbus chain. _nextBus = _busList; _busList = this; - const char* errorStrings[] = {"success", "invalid id", "invalid buffer", "invalid quantity", "response timeout", "frame error", "crc error", "unknown comm error", "unexpected id", "exception response", "unexpected function code", "unexpected response length", "unexpected byte count", "unexpected address", "unexpected value", "unexpected quantity"}; } @@ -524,7 +523,7 @@ void Modbus::_loop(unsigned long currentMicros) { } _cycleStartTime = _currentMicros; if (_currentNode == NULL) return; - + const char* errorStrings[] = {"success", "invalid id", "invalid buffer", "invalid quantity", "response timeout", "frame error", "crc error", "unknown comm error", "unexpected id", "exception response", "unexpected function code", "unexpected response length", "unexpected byte count", "unexpected address", "unexpected value", "unexpected quantity"}; uint8_t error; error = modbusmaster->writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); if (error != 0) DIAG(F("ModbusHR: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), &errorStrings[error]); From 9ca731cecdec6b69888cb341b5802dc2e2cb7358 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Mon, 25 Nov 2024 05:32:30 -0500 Subject: [PATCH 09/73] fix comment --- IO_Modbus.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 259429f0..02e8b37b 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -538,7 +538,7 @@ void Modbus::_loop(unsigned long currentMicros) { if (error != 0) DIAG(F("ModbusIR: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), &errorStrings[error]); } -// Link to chain of CMRI bus instances +// Link to chain of Modbus instances Modbus *Modbus::_busList = NULL; From 62de9d9796b5714159aac454a4654739a6351d2c Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Mon, 25 Nov 2024 05:50:33 -0500 Subject: [PATCH 10/73] fix function names: getNumDiscreteInputs --- IO_Modbus.cpp | 4 ++-- IO_Modbus.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 02e8b37b..757e3ced 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -531,8 +531,8 @@ void Modbus::_loop(unsigned long currentMicros) { error = modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, _currentNode->coils, _currentNode->getNumCoils()); if (error != 0) DIAG(F("ModbusMC: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), &errorStrings[error]); - error = modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, _currentNode->discreteInputs, _currentNode->getNumDisInputs()); - if (error != 0) DIAG(F("ModbusDI: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDisInputs(), &errorStrings[error]); + error = modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); + if (error != 0) DIAG(F("ModbusDI: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), &errorStrings[error]); error = modbusmaster->readInputRegisters(_currentNode->getNodeID(), 0, _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); if (error != 0) DIAG(F("ModbusIR: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), &errorStrings[error]); diff --git a/IO_Modbus.h b/IO_Modbus.h index 7590e874..6cd82713 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -210,7 +210,7 @@ class Modbusnode : public IODevice { uint8_t getNumCoils() { return numCoils; } - uint8_t getNumDisInputs() { + uint8_t getNumDiscreteInputs() { return numDiscreteInputs; } uint8_t getNumHoldingRegisters() { From 44486605a55372b20a1a7f939a6ff69dd0b4c741 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Mon, 25 Nov 2024 10:43:17 -0500 Subject: [PATCH 11/73] fix vpin numbers --- IO_Modbus.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/IO_Modbus.h b/IO_Modbus.h index 6cd82713..004d82e6 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -247,13 +247,13 @@ class Modbusnode : public IODevice { int _readAnalogue(VPIN vpin) override { // Return acquired data value, e.g. - int pin = vpin - _firstVpin; + int pin = vpin - _firstVpin - numDiscreteInputs; return inputRegisters[pin]; } void _write(VPIN vpin, int value) override { // Update current state for this device, in preparation the bus transmission - uint16_t pin = vpin - _firstVpin - numDiscreteInputs; + uint16_t pin = vpin - _firstVpin - numDiscreteInputs - numInputRegisters; if (pin < numCoils) { if (value) coils[pin] = value; @@ -263,7 +263,7 @@ class Modbusnode : public IODevice { } void writeAnalogue(VPIN vpin, int value) { - uint16_t pin = vpin - _firstVpin - numInputRegisters; + uint16_t pin = vpin - _firstVpin - numDiscreteInputs - numInputRegisters - numCoils; if (pin < numHoldingRegisters) { if (value) holdingRegisters[pin] = value; From efdc14539b5efb2e180ec8c16debe0fc30eb9245 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Mon, 25 Nov 2024 13:23:44 -0500 Subject: [PATCH 12/73] i don't know what was fixed --- IO_Modbus.cpp | 12 +++++++----- IO_Modbus.h | 1 - 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 757e3ced..01bbc4a4 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -520,22 +520,24 @@ void Modbus::_loop(unsigned long currentMicros) { if (_currentNode == NULL) { // If we're between read/write cycles then don't do anything else. if (_currentMicros - _cycleStartTime < _cycleTime) return; + _currentNode = _nodeListStart; } _cycleStartTime = _currentMicros; if (_currentNode == NULL) return; - const char* errorStrings[] = {"success", "invalid id", "invalid buffer", "invalid quantity", "response timeout", "frame error", "crc error", "unknown comm error", "unexpected id", "exception response", "unexpected function code", "unexpected response length", "unexpected byte count", "unexpected address", "unexpected value", "unexpected quantity"}; + const char* errorStrings[16] = { "success", "invalid id", "invalid buffer", "invalid quantity", "response timeout", "frame error", "crc error", "unknown comm error", "unexpected id", "exception response", "unexpected function code", "unexpected response length", "unexpected byte count", "unexpected address", "unexpected value", "unexpected quantity" }; + uint8_t error; error = modbusmaster->writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); - if (error != 0) DIAG(F("ModbusHR: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), &errorStrings[error]); + if (error != 0) DIAG(F("ModbusHR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); error = modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, _currentNode->coils, _currentNode->getNumCoils()); - if (error != 0) DIAG(F("ModbusMC: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), &errorStrings[error]); + if (error != 0) DIAG(F("ModbusMC: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); error = modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); - if (error != 0) DIAG(F("ModbusDI: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), &errorStrings[error]); + if (error != 0) DIAG(F("ModbusDI: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); error = modbusmaster->readInputRegisters(_currentNode->getNodeID(), 0, _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); - if (error != 0) DIAG(F("ModbusIR: %02d %04d %04d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), &errorStrings[error]); + if (error != 0) DIAG(F("ModbusIR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); } // Link to chain of Modbus instances diff --git a/IO_Modbus.h b/IO_Modbus.h index 004d82e6..ee06d558 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -345,7 +345,6 @@ class Modbus : public IODevice { HardwareSerial *_serial; ModbusRTUMaster *modbusmaster; - const char* errorStrings[]; // Device-specific initialisation void _begin() override { ModbusRTUMaster modbusmaster(*_serial, _transmitEnablePin); From 0b5aca43f8ed84ebd566692932ff9a25d8d19d82 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Mon, 25 Nov 2024 17:13:51 -0500 Subject: [PATCH 13/73] still not working... --- IO_Modbus.cpp | 43 ++++++++++++++++++++++++++++--------------- IO_Modbus.h | 13 +++---------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 01bbc4a4..876590aa 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -253,7 +253,7 @@ void ModbusRTUComm::writeAdu(ModbusADU& adu) { if (_rePin >= 0) digitalWrite(_rePin, HIGH); _serial.write(adu.rtu, adu.getRtuLen()); _serial.flush(); - delayMicroseconds(_postDelay); + ///delayMicroseconds(_postDelay); if (_dePin >= 0) digitalWrite(_dePin, LOW); if (_rePin >= 0) digitalWrite(_rePin, LOW); } @@ -498,7 +498,9 @@ Modbus::Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_ //ArduinoPins::fastWriteDigital(_transmitEnablePin, 0); // transmitter initially off //} - + //_serial->begin(baud); + //_modbusmaster.begin(baud); + //DIAG(F("ModbusInit: %d %d"), _transmitEnablePin, _baud); // Add device to HAL device chain IODevice::addDevice(this); @@ -507,8 +509,16 @@ Modbus::Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_ _busList = this; } +void Modbus::_begin() { + ModbusRTUMaster _modbusmaster(*_serial, _transmitEnablePin, -1); + _serial->begin(_baud); + _modbusmaster.begin(_baud); +#if defined(DIAG_IO) + _display(); +#endif +} -// Main loop function for CMRIbus. +// Main loop function for Modbus. // Work through list of nodes. For each node, in separate loop entries // send initialisation message (once only); then send // output message; then send prompt for input data, and @@ -517,27 +527,30 @@ Modbus::Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_ void Modbus::_loop(unsigned long currentMicros) { _currentMicros = currentMicros; - if (_currentNode == NULL) { - // If we're between read/write cycles then don't do anything else. - if (_currentMicros - _cycleStartTime < _cycleTime) return; - _currentNode = _nodeListStart; - } - _cycleStartTime = _currentMicros; + + //if (_currentNode == NULL) { + //_currentNode = _nodeListStart; + + //} + if (_currentMicros - _cycleStartTime < _cycleTime) return; if (_currentNode == NULL) return; + const char* errorStrings[16] = { "success", "invalid id", "invalid buffer", "invalid quantity", "response timeout", "frame error", "crc error", "unknown comm error", "unexpected id", "exception response", "unexpected function code", "unexpected response length", "unexpected byte count", "unexpected address", "unexpected value", "unexpected quantity" }; uint8_t error; - error = modbusmaster->writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); - if (error != 0) DIAG(F("ModbusHR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); + //error = _modbusmaster->writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); + //if (error != 0) DIAG(F("ModbusHR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); - error = modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, _currentNode->coils, _currentNode->getNumCoils()); + error = _modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, _currentNode->coils, _currentNode->getNumCoils()); if (error != 0) DIAG(F("ModbusMC: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); - error = modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); + error = _modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); if (error != 0) DIAG(F("ModbusDI: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); - error = modbusmaster->readInputRegisters(_currentNode->getNodeID(), 0, _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); - if (error != 0) DIAG(F("ModbusIR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); + //error = _modbusmaster->readInputRegisters(_currentNode->getNodeID(), 0, _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); + //if (error != 0) DIAG(F("ModbusIR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); + //delayUntil(_currentMicros + _cycleTime * 1000UL); + _cycleStartTime = _currentMicros; } // Link to chain of Modbus instances diff --git a/IO_Modbus.h b/IO_Modbus.h index ee06d558..8d6b940a 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -339,21 +339,14 @@ class Modbus : public IODevice { static Modbus *_busList; // linked list of defined bus instances public: - static void create(uint8_t busNo, HardwareSerial& serial, unsigned long baud, uint16_t cycleTimeMS=500, int16_t transmitEnablePin=VPIN_NONE) { + static void create(uint8_t busNo, HardwareSerial& serial, unsigned long baud, uint16_t cycleTimeMS=500, int16_t transmitEnablePin=51) { new Modbus(busNo, serial, baud, cycleTimeMS, transmitEnablePin); } HardwareSerial *_serial; - ModbusRTUMaster *modbusmaster; + ModbusRTUMaster *_modbusmaster; // Device-specific initialisation - void _begin() override { - ModbusRTUMaster modbusmaster(*_serial, _transmitEnablePin); - _serial->begin(_baud, SERIAL_8N1); - modbusmaster.begin(_baud); - #if defined(DIAG_IO) - _display(); - #endif - } + void _begin() override; // Loop function (overriding IODevice::_loop(unsigned long)) void _loop(unsigned long currentMicros) override; From 8624b0aa7944ad25ec37f592651f4029f85b1e2b Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Tue, 26 Nov 2024 08:18:22 -0500 Subject: [PATCH 14/73] making current --- IO_Modbus.cpp | 69 +++++++++++++++++++++++++------------------------- IO_Modbus.h | 70 ++++++++++++++++++++++++++++----------------------- 2 files changed, 74 insertions(+), 65 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 876590aa..52b9331f 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -206,11 +206,11 @@ void ModbusRTUComm::begin(unsigned long baud, uint32_t config) { #endif if (_dePin >= 0) { pinMode(_dePin, OUTPUT); - digitalWrite(_dePin, LOW); + ArduinoPins::fastWriteDigital(_dePin, LOW); } if (_rePin >= 0) { pinMode(_rePin, OUTPUT); - digitalWrite(_rePin, LOW); + ArduinoPins::fastWriteDigital(_rePin, LOW); } clearRxBuffer(); } @@ -249,13 +249,13 @@ ModbusRTUCommError ModbusRTUComm::readAdu(ModbusADU& adu) { void ModbusRTUComm::writeAdu(ModbusADU& adu) { adu.updateCrc(); - if (_dePin >= 0) digitalWrite(_dePin, HIGH); - if (_rePin >= 0) digitalWrite(_rePin, HIGH); + if (_dePin >= 0) ArduinoPins::fastWriteDigital(_dePin, HIGH); + if (_rePin >= 0) ArduinoPins::fastWriteDigital(_rePin, HIGH); _serial.write(adu.rtu, adu.getRtuLen()); _serial.flush(); ///delayMicroseconds(_postDelay); - if (_dePin >= 0) digitalWrite(_dePin, LOW); - if (_rePin >= 0) digitalWrite(_rePin, LOW); + if (_dePin >= 0) ArduinoPins::fastWriteDigital(_dePin, LOW); + if (_rePin >= 0) ArduinoPins::fastWriteDigital(_rePin, LOW); } void ModbusRTUComm::clearRxBuffer() { @@ -487,10 +487,10 @@ ModbusRTUMasterError ModbusRTUMaster::_translateCommError(ModbusRTUCommError com ************************************************************/ // Constructor for Modbus -Modbus::Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_t cycleTimeMS, int16_t transmitEnablePin) { +Modbus::Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_t cycleTimeMS, int8_t transmitEnablePin) { _busNo = busNo; _baud = baud; - _serial = &serial; + _serialD = &serial; _cycleTime = cycleTimeMS * 1000UL; // convert from milliseconds to microseconds. _transmitEnablePin = transmitEnablePin; //if (_transmitEnablePin != VPIN_NONE) { @@ -503,21 +503,12 @@ Modbus::Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_ //DIAG(F("ModbusInit: %d %d"), _transmitEnablePin, _baud); // Add device to HAL device chain IODevice::addDevice(this); - + // Add bus to CMRIbus chain. _nextBus = _busList; _busList = this; } -void Modbus::_begin() { - ModbusRTUMaster _modbusmaster(*_serial, _transmitEnablePin, -1); - _serial->begin(_baud); - _modbusmaster.begin(_baud); -#if defined(DIAG_IO) - _display(); -#endif -} - // Main loop function for Modbus. // Work through list of nodes. For each node, in separate loop entries // send initialisation message (once only); then send @@ -528,30 +519,35 @@ void Modbus::_loop(unsigned long currentMicros) { _currentMicros = currentMicros; - //if (_currentNode == NULL) { - //_currentNode = _nodeListStart; + if (_currentNode == NULL) { + _currentNode = _nodeListStart; - //} + } + //DIAG(F("Modbus Loop: %d : %d :: %d"), _currentMicros, _cycleStartTime, _currentNode); if (_currentMicros - _cycleStartTime < _cycleTime) return; + _cycleStartTime = _currentMicros; + DIAG(F("Tick")); if (_currentNode == NULL) return; const char* errorStrings[16] = { "success", "invalid id", "invalid buffer", "invalid quantity", "response timeout", "frame error", "crc error", "unknown comm error", "unexpected id", "exception response", "unexpected function code", "unexpected response length", "unexpected byte count", "unexpected address", "unexpected value", "unexpected quantity" }; uint8_t error; //error = _modbusmaster->writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); - //if (error != 0) DIAG(F("ModbusHR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); - - error = _modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, _currentNode->coils, _currentNode->getNumCoils()); - if (error != 0) DIAG(F("ModbusMC: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); + //DIAG(F("ModbusHR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); + //error = _modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, _currentNode->coils, _currentNode->getNumCoils()); + DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); + if (error == MODBUS_RTU_MASTER_EXCEPTION_RESPONSE) { + DIAG(F(": %s"), _modbusmaster->getExceptionResponse()); + } error = _modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); - if (error != 0) DIAG(F("ModbusDI: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); + DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); //error = _modbusmaster->readInputRegisters(_currentNode->getNodeID(), 0, _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); - //if (error != 0) DIAG(F("ModbusIR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); + //DIAG(F("ModbusIR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); + _currentNode = _currentNode->getNext(); //delayUntil(_currentMicros + _cycleTime * 1000UL); - _cycleStartTime = _currentMicros; -} + } // Link to chain of Modbus instances Modbus *Modbus::_busList = NULL; @@ -567,10 +563,14 @@ Modbusnode::Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, _nPins = nPins; _busNo = busNo; _nodeID = nodeID; - coils[numCoils]; - discreteInputs[numDiscreteInputs]; - holdingRegisters[numHoldingRegisters]; - inputRegisters[numInputRegisters]; + _numCoils = numCoils; + _numDiscreteInputs = numDiscreteInputs; + _numHoldingRegisters = numHoldingRegisters; + _numInputRegisters = numInputRegisters; + coils[_numCoils]; + discreteInputs[_numDiscreteInputs]; + holdingRegisters[_numHoldingRegisters]; + inputRegisters[_numInputRegisters]; if ((unsigned int)_nPins < numDiscreteInputs + numCoils) DIAG(F("Modbusnode: bus:%d nodeID:%d WARNING number of Vpins does not cover all inputs and outputs"), _busNo, _nodeID); @@ -582,11 +582,12 @@ Modbusnode::Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, // Add this device to HAL device list IODevice::addDevice(this); - + _display(); // Add Modbusnode to Modbus object. Modbus *bus = Modbus::findBus(_busNo); if (bus != NULL) { bus->addNode(this); return; } + } diff --git a/IO_Modbus.h b/IO_Modbus.h index 8d6b940a..262900c3 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -189,16 +189,16 @@ class Modbusnode : public IODevice { char _type; Modbusnode *_next = NULL; bool _initialised = false; - uint8_t numCoils; - uint8_t numDiscreteInputs; - uint8_t numHoldingRegisters; - uint8_t numInputRegisters; + uint8_t _numCoils; + uint8_t _numDiscreteInputs; + uint8_t _numHoldingRegisters; + uint8_t _numInputRegisters; public: static void create(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils=0, uint8_t numDiscreteInputs=0, uint8_t numHoldingRegisters=0, uint8_t numInputRegisters=0) { if (checkNoOverlap(firstVpin, nPins)) new Modbusnode(firstVpin, nPins, busNo, nodeID, numCoils, numDiscreteInputs, numHoldingRegisters, numInputRegisters); } - Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils=0, uint8_t numDiscreteInputs=0, uint8_t numHoldingRegisters=0, uint8_t numInputRegisters=0); + Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils, uint8_t numDiscreteInputs, uint8_t numHoldingRegisters, uint8_t numInputRegisters); bool *coils; bool *discreteInputs; uint16_t *holdingRegisters; @@ -208,16 +208,16 @@ class Modbusnode : public IODevice { return _nodeID; } uint8_t getNumCoils() { - return numCoils; + return _numCoils; } uint8_t getNumDiscreteInputs() { - return numDiscreteInputs; + return _numDiscreteInputs; } uint8_t getNumHoldingRegisters() { - return numHoldingRegisters; + return _numHoldingRegisters; } uint8_t getNumInputRegisters() { - return numInputRegisters; + return _numInputRegisters; } Modbusnode *getNext() { return _next; @@ -232,14 +232,14 @@ class Modbusnode : public IODevice { _initialised = true; } - void _begin() { + void _begin() override { _initialised = false; } int _read(VPIN vpin) override { // Return current state from this device uint16_t pin = vpin - _firstVpin; - if (pin < numDiscreteInputs) { + if (pin < _numDiscreteInputs) { return discreteInputs[pin]; } else return 0; @@ -247,14 +247,14 @@ class Modbusnode : public IODevice { int _readAnalogue(VPIN vpin) override { // Return acquired data value, e.g. - int pin = vpin - _firstVpin - numDiscreteInputs; + int pin = vpin - _firstVpin - _numDiscreteInputs; return inputRegisters[pin]; } void _write(VPIN vpin, int value) override { // Update current state for this device, in preparation the bus transmission - uint16_t pin = vpin - _firstVpin - numDiscreteInputs - numInputRegisters; - if (pin < numCoils) { + uint16_t pin = vpin - _firstVpin - _numDiscreteInputs - _numInputRegisters; + if (pin < _numCoils) { if (value) coils[pin] = value; else @@ -262,9 +262,9 @@ class Modbusnode : public IODevice { } } - void writeAnalogue(VPIN vpin, int value) { - uint16_t pin = vpin - _firstVpin - numDiscreteInputs - numInputRegisters - numCoils; - if (pin < numHoldingRegisters) { + void _writeAnalogue(VPIN vpin, int value, uint8_t param1=0, uint16_t param2=0) override { + uint16_t pin = vpin - _firstVpin - _numDiscreteInputs - _numInputRegisters - _numCoils; + if (pin < _numHoldingRegisters) { if (value) holdingRegisters[pin] = value; else @@ -273,23 +273,23 @@ class Modbusnode : public IODevice { } void saveIncomingData(uint8_t index, uint8_t data) { - if (index < numDiscreteInputs) + if (index < _numDiscreteInputs) discreteInputs[index] = data; } uint8_t getOutputStates(uint8_t index) { - if (index < numCoils) + if (index < _numCoils) return coils[index]; else return 0; } uint16_t getNumInputs() { - return numDiscreteInputs; + return _numDiscreteInputs; } uint16_t getNumOutputs() { - return numCoils; + return _numCoils; } char getType() { @@ -301,9 +301,9 @@ class Modbusnode : public IODevice { } void _display() override { - DIAG(F("Modbusnode type:'%c' configured on bus:%d nodeID:%d VPINs:%u-%u (in) %u-%u (out)"), - _type, _busNo, _nodeID, _firstVpin, _firstVpin+numDiscreteInputs-1, - _firstVpin+numDiscreteInputs, _firstVpin+numDiscreteInputs+numCoils-1); + DIAG(F("Modbusnode configured on bus:%d nodeID:%d VPINs:%u-%u (in) %u-%u (out)"), + _busNo, _nodeID, _firstVpin, _firstVpin+_numDiscreteInputs-1, + _firstVpin+_numDiscreteInputs, _firstVpin+_numDiscreteInputs+_numCoils-1); } }; @@ -322,7 +322,7 @@ class Modbus : public IODevice { uint8_t _busNo; unsigned long _baud; - int16_t _transmitEnablePin = VPIN_NONE; + int8_t _transmitEnablePin; Modbusnode *_nodeListStart = NULL, *_nodeListEnd = NULL; Modbusnode *_currentNode = NULL; @@ -339,21 +339,29 @@ class Modbus : public IODevice { static Modbus *_busList; // linked list of defined bus instances public: - static void create(uint8_t busNo, HardwareSerial& serial, unsigned long baud, uint16_t cycleTimeMS=500, int16_t transmitEnablePin=51) { + static void create(uint8_t busNo, HardwareSerial& serial, unsigned long baud, uint16_t cycleTimeMS=500, int8_t transmitEnablePin=0) { new Modbus(busNo, serial, baud, cycleTimeMS, transmitEnablePin); } - HardwareSerial *_serial; + HardwareSerial *_serialD; ModbusRTUMaster *_modbusmaster; // Device-specific initialisation - void _begin() override; + void _begin() override { + ModbusRTUMaster _modbusmaster(*_serialD, _transmitEnablePin, -1); + _serialD->begin(_baud); + _modbusmaster.begin(_baud); + #if defined(DIAG_IO) + _display(); + #endif + } // Loop function (overriding IODevice::_loop(unsigned long)) void _loop(unsigned long currentMicros) override; // Display information about the device void _display() override { - DIAG(F("Modbus %d configured, speed=%d baud, cycle=%d ms"), _busNo, _baud, _cycleTime/1000); + DIAG(F("Modbus Configured on %d Vpins:%d-%d %S"), _transmitEnablePin, _firstVpin, _firstVpin+_nPins-1, + _deviceState == DEVSTATE_FAILED ? F("OFFLINE") : F("OK")); } // Locate Modbusnode object with specified nodeID. @@ -373,11 +381,11 @@ class Modbus : public IODevice { _nodeListEnd = newNode; else _nodeListEnd->setNext(newNode); - DIAG(F("bus: 260h nodeID: _nodeListStart:%d _nodeListEnd:%d"), _nodeListStart, _nodeListEnd); + //DIAG(F("Modbus: 260h nodeID:%d _nodeListStart:%d _nodeListEnd:%d"), newNode, _nodeListStart, _nodeListEnd); } protected: - Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_t cycleTimeMS, int16_t transmitEnablePin); + Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_t cycleTimeMS, int8_t transmitEnablePin); public: From 8afb11f52091b70843c90daf8d8ab89454a379ca Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Tue, 26 Nov 2024 15:38:41 -0500 Subject: [PATCH 15/73] some interesting results... --- IO_Modbus.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 52b9331f..8968dcd4 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -532,15 +532,15 @@ void Modbus::_loop(unsigned long currentMicros) { const char* errorStrings[16] = { "success", "invalid id", "invalid buffer", "invalid quantity", "response timeout", "frame error", "crc error", "unknown comm error", "unexpected id", "exception response", "unexpected function code", "unexpected response length", "unexpected byte count", "unexpected address", "unexpected value", "unexpected quantity" }; uint8_t error; - //error = _modbusmaster->writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); - //DIAG(F("ModbusHR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); + error = _modbusmaster->writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); + DIAG(F("ModbusHR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); //error = _modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, _currentNode->coils, _currentNode->getNumCoils()); DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); if (error == MODBUS_RTU_MASTER_EXCEPTION_RESPONSE) { DIAG(F(": %s"), _modbusmaster->getExceptionResponse()); } - error = _modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); + //error = _modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); //error = _modbusmaster->readInputRegisters(_currentNode->getNodeID(), 0, _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); From 83130f0c4ac6d76f3c55a89739982d7d9cc37dad Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Tue, 26 Nov 2024 17:18:14 -0500 Subject: [PATCH 16/73] fixed some errors --- IO_Modbus.cpp | 30 ++++++++++++----------------- IO_Modbus.h | 53 +++++++++++++++++---------------------------------- 2 files changed, 30 insertions(+), 53 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 8968dcd4..d9f37a1d 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -282,11 +282,11 @@ void ModbusRTUMaster::begin(unsigned long baud, uint32_t config) { -ModbusRTUMasterError ModbusRTUMaster::readCoils(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity) { +ModbusRTUMasterError ModbusRTUMaster::readCoils(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity) { return _readValues(id, 1, startAddress, buf, quantity); } -ModbusRTUMasterError ModbusRTUMaster::readDiscreteInputs(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity) { +ModbusRTUMasterError ModbusRTUMaster::readDiscreteInputs(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity) { return _readValues(id, 2, startAddress, buf, quantity); } @@ -300,7 +300,7 @@ ModbusRTUMasterError ModbusRTUMaster::readInputRegisters(uint8_t id, uint16_t st -ModbusRTUMasterError ModbusRTUMaster::writeSingleCoil(uint8_t id, uint16_t address, bool value) { +ModbusRTUMasterError ModbusRTUMaster::writeSingleCoil(uint8_t id, uint16_t address, char value) { return _writeSingleValue(id, 5, address, ((value) ? 0xFF00 : 0x0000)); } @@ -310,7 +310,7 @@ ModbusRTUMasterError ModbusRTUMaster::writeSingleHoldingRegister(uint8_t id, uin -ModbusRTUMasterError ModbusRTUMaster::writeMultipleCoils(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity) { +ModbusRTUMasterError ModbusRTUMaster::writeMultipleCoils(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity) { const uint8_t functionCode = 15; if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; @@ -385,7 +385,7 @@ uint8_t ModbusRTUMaster::getExceptionResponse() { -ModbusRTUMasterError ModbusRTUMaster::_readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, bool buf[], uint16_t quantity) { +ModbusRTUMasterError ModbusRTUMaster::_readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, char buf[], uint16_t quantity) { if (id < 1 || id > 247) return MODBUS_RTU_MASTER_INVALID_ID; if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; if (quantity == 0 || quantity > 2000) return MODBUS_RTU_MASTER_INVALID_QUANTITY; @@ -526,25 +526,22 @@ void Modbus::_loop(unsigned long currentMicros) { //DIAG(F("Modbus Loop: %d : %d :: %d"), _currentMicros, _cycleStartTime, _currentNode); if (_currentMicros - _cycleStartTime < _cycleTime) return; _cycleStartTime = _currentMicros; - DIAG(F("Tick")); if (_currentNode == NULL) return; const char* errorStrings[16] = { "success", "invalid id", "invalid buffer", "invalid quantity", "response timeout", "frame error", "crc error", "unknown comm error", "unexpected id", "exception response", "unexpected function code", "unexpected response length", "unexpected byte count", "unexpected address", "unexpected value", "unexpected quantity" }; uint8_t error; - error = _modbusmaster->writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); + error = _modbusmaster->writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); DIAG(F("ModbusHR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); - //error = _modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, _currentNode->coils, _currentNode->getNumCoils()); + error = _modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, (char*) _currentNode->coils, _currentNode->getNumCoils()); DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); - if (error == MODBUS_RTU_MASTER_EXCEPTION_RESPONSE) { - DIAG(F(": %s"), _modbusmaster->getExceptionResponse()); - } - //error = _modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); + + error = _modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, (char*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); - //error = _modbusmaster->readInputRegisters(_currentNode->getNodeID(), 0, _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); - //DIAG(F("ModbusIR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); + error = _modbusmaster->readInputRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); + DIAG(F("ModbusIR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); _currentNode = _currentNode->getNext(); //delayUntil(_currentMicros + _cycleTime * 1000UL); } @@ -567,10 +564,7 @@ Modbusnode::Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, _numDiscreteInputs = numDiscreteInputs; _numHoldingRegisters = numHoldingRegisters; _numInputRegisters = numInputRegisters; - coils[_numCoils]; - discreteInputs[_numDiscreteInputs]; - holdingRegisters[_numHoldingRegisters]; - inputRegisters[_numInputRegisters]; + if ((unsigned int)_nPins < numDiscreteInputs + numCoils) DIAG(F("Modbusnode: bus:%d nodeID:%d WARNING number of Vpins does not cover all inputs and outputs"), _busNo, _nodeID); diff --git a/IO_Modbus.h b/IO_Modbus.h index 262900c3..a1df45e0 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -149,14 +149,14 @@ class ModbusRTUMaster { void setTimeout(unsigned long timeout); void begin(unsigned long baud, uint32_t config = SERIAL_8N1); - ModbusRTUMasterError readCoils(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity); - ModbusRTUMasterError readDiscreteInputs(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity); + ModbusRTUMasterError readCoils(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity); + ModbusRTUMasterError readDiscreteInputs(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity); ModbusRTUMasterError readHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); ModbusRTUMasterError readInputRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); - ModbusRTUMasterError writeSingleCoil(uint8_t id, uint16_t address, bool value); + ModbusRTUMasterError writeSingleCoil(uint8_t id, uint16_t address, char value); ModbusRTUMasterError writeSingleHoldingRegister(uint8_t id, uint16_t address, uint16_t value); - ModbusRTUMasterError writeMultipleCoils(uint8_t id, uint16_t startAddress, bool buf[], uint16_t quantity); + ModbusRTUMasterError writeMultipleCoils(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity); ModbusRTUMasterError writeMultipleHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); uint8_t getExceptionResponse(); @@ -165,7 +165,7 @@ class ModbusRTUMaster { ModbusRTUComm _rtuComm; uint8_t _exceptionResponse = 0; - ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, bool buf[], uint16_t quantity); + ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, char buf[], uint16_t quantity); ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity); ModbusRTUMasterError _writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value); @@ -199,10 +199,10 @@ class Modbusnode : public IODevice { if (checkNoOverlap(firstVpin, nPins)) new Modbusnode(firstVpin, nPins, busNo, nodeID, numCoils, numDiscreteInputs, numHoldingRegisters, numInputRegisters); } Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils, uint8_t numDiscreteInputs, uint8_t numHoldingRegisters, uint8_t numInputRegisters); - bool *coils; - bool *discreteInputs; - uint16_t *holdingRegisters; - uint16_t *inputRegisters; + char *coils[100]; + char *discreteInputs[100]; + uint16_t *holdingRegisters[100]; + uint16_t *inputRegisters[100]; uint8_t getNodeID() { return _nodeID; @@ -240,7 +240,7 @@ class Modbusnode : public IODevice { // Return current state from this device uint16_t pin = vpin - _firstVpin; if (pin < _numDiscreteInputs) { - return discreteInputs[pin]; + return discreteInputs[pin]? 1:0; } else return 0; } @@ -248,7 +248,7 @@ class Modbusnode : public IODevice { int _readAnalogue(VPIN vpin) override { // Return acquired data value, e.g. int pin = vpin - _firstVpin - _numDiscreteInputs; - return inputRegisters[pin]; + return (int) inputRegisters[pin]; } void _write(VPIN vpin, int value) override { @@ -256,7 +256,9 @@ class Modbusnode : public IODevice { uint16_t pin = vpin - _firstVpin - _numDiscreteInputs - _numInputRegisters; if (pin < _numCoils) { if (value) - coils[pin] = value; + if (value == 1) coils[pin] = (char*) 0x1; + if (value == 0) coils[pin] = (char*) 0x0; + //coils[pin] = value; else coils[pin]; } @@ -266,32 +268,12 @@ class Modbusnode : public IODevice { uint16_t pin = vpin - _firstVpin - _numDiscreteInputs - _numInputRegisters - _numCoils; if (pin < _numHoldingRegisters) { if (value) - holdingRegisters[pin] = value; + holdingRegisters[pin] = (uint16_t*) value; else holdingRegisters[pin]; } } - void saveIncomingData(uint8_t index, uint8_t data) { - if (index < _numDiscreteInputs) - discreteInputs[index] = data; - } - - uint8_t getOutputStates(uint8_t index) { - if (index < _numCoils) - return coils[index]; - else - return 0; - } - - uint16_t getNumInputs() { - return _numDiscreteInputs; - } - - uint16_t getNumOutputs() { - return _numCoils; - } - char getType() { return _type; } @@ -348,8 +330,9 @@ class Modbus : public IODevice { // Device-specific initialisation void _begin() override { ModbusRTUMaster _modbusmaster(*_serialD, _transmitEnablePin, -1); - _serialD->begin(_baud); - _modbusmaster.begin(_baud); + _serialD->begin(_baud, SERIAL_8N1); + //_serialD->println("test"); + _modbusmaster.begin(_baud, SERIAL_8N1); #if defined(DIAG_IO) _display(); #endif From 5a4b87846c2f14281bfadc00f7115fd1bceb223a Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Wed, 27 Nov 2024 04:57:47 -0500 Subject: [PATCH 17/73] some type fixes --- IO_Modbus.cpp | 22 +++++++++++----------- IO_Modbus.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index d9f37a1d..79279ae8 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -206,11 +206,11 @@ void ModbusRTUComm::begin(unsigned long baud, uint32_t config) { #endif if (_dePin >= 0) { pinMode(_dePin, OUTPUT); - ArduinoPins::fastWriteDigital(_dePin, LOW); + digitalWrite(_dePin, LOW); } if (_rePin >= 0) { pinMode(_rePin, OUTPUT); - ArduinoPins::fastWriteDigital(_rePin, LOW); + digitalWrite(_rePin, LOW); } clearRxBuffer(); } @@ -249,13 +249,13 @@ ModbusRTUCommError ModbusRTUComm::readAdu(ModbusADU& adu) { void ModbusRTUComm::writeAdu(ModbusADU& adu) { adu.updateCrc(); - if (_dePin >= 0) ArduinoPins::fastWriteDigital(_dePin, HIGH); - if (_rePin >= 0) ArduinoPins::fastWriteDigital(_rePin, HIGH); + if (_dePin >= 0) digitalWrite(_dePin, HIGH); + if (_rePin >= 0) digitalWrite(_rePin, HIGH); _serial.write(adu.rtu, adu.getRtuLen()); _serial.flush(); ///delayMicroseconds(_postDelay); - if (_dePin >= 0) ArduinoPins::fastWriteDigital(_dePin, LOW); - if (_rePin >= 0) ArduinoPins::fastWriteDigital(_rePin, LOW); + if (_dePin >= 0) digitalWrite(_dePin, LOW); + if (_rePin >= 0) digitalWrite(_rePin, LOW); } void ModbusRTUComm::clearRxBuffer() { @@ -323,7 +323,7 @@ ModbusRTUMasterError ModbusRTUMaster::writeMultipleCoils(uint8_t id, uint16_t st adu.setDataRegister(2, quantity); adu.data[4] = byteCount; for (uint16_t i = 0; i < quantity; i++) { - bitWrite(adu.data[5 + (i >> 3)], i & 7, buf[i]); + bitWrite(adu.data[5 + (i >> 3)], i & 7, (bool) buf[i]); } for (uint16_t i = quantity; i < (byteCount * 8); i++) { bitClear(adu.data[5 + (i >> 3)], i & 7); @@ -408,7 +408,7 @@ ModbusRTUMasterError ModbusRTUMaster::_readValues(uint8_t id, uint8_t functionCo if (adu.getDataLen() != (1 + byteCount)) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; if (adu.data[0] != byteCount) return MODBUS_RTU_MASTER_UNEXPECTED_BYTE_COUNT; for (uint16_t i = 0; i < quantity; i++) { - buf[i] = bitRead(adu.data[1 + (i >> 3)], i & 7); + buf[i] = (char) bitRead(adu.data[1 + (i >> 3)], i & 7); } return MODBUS_RTU_MASTER_SUCCESS; } @@ -531,16 +531,16 @@ void Modbus::_loop(unsigned long currentMicros) { const char* errorStrings[16] = { "success", "invalid id", "invalid buffer", "invalid quantity", "response timeout", "frame error", "crc error", "unknown comm error", "unexpected id", "exception response", "unexpected function code", "unexpected response length", "unexpected byte count", "unexpected address", "unexpected value", "unexpected quantity" }; uint8_t error; - error = _modbusmaster->writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); + //error = _modbusmaster->writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); DIAG(F("ModbusHR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); - error = _modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, (char*) _currentNode->coils, _currentNode->getNumCoils()); + //error = _modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, (char*) _currentNode->coils, _currentNode->getNumCoils()); DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); error = _modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, (char*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); - error = _modbusmaster->readInputRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); + //error = _modbusmaster->readInputRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); DIAG(F("ModbusIR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); _currentNode = _currentNode->getNext(); //delayUntil(_currentMicros + _cycleTime * 1000UL); diff --git a/IO_Modbus.h b/IO_Modbus.h index a1df45e0..d5a0bbcf 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -240,7 +240,7 @@ class Modbusnode : public IODevice { // Return current state from this device uint16_t pin = vpin - _firstVpin; if (pin < _numDiscreteInputs) { - return discreteInputs[pin]? 1:0; + return (int) discreteInputs[pin]; } else return 0; } From 5c40577703454099b66cf970d8c1fddc7a39f3ea Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Wed, 27 Nov 2024 07:35:00 -0500 Subject: [PATCH 18/73] updating --- IO_Modbus.cpp | 23 ++++++++++++++--------- IO_Modbus.h | 30 +++++++++++++++--------------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 79279ae8..7942bb7e 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -486,13 +486,16 @@ ModbusRTUMasterError ModbusRTUMaster::_translateCommError(ModbusRTUCommError com * Modbus implementation ************************************************************/ + // Constructor for Modbus -Modbus::Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_t cycleTimeMS, int8_t transmitEnablePin) { - _busNo = busNo; +Modbus::Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_t cycleTimeMS, int8_t _dePin) { + + //serial.write("\002\004",2); + ModbusRTUMaster _modbusmaster(serial, _dePin); _baud = baud; _serialD = &serial; + _busNo = busNo; _cycleTime = cycleTimeMS * 1000UL; // convert from milliseconds to microseconds. - _transmitEnablePin = transmitEnablePin; //if (_transmitEnablePin != VPIN_NONE) { //pinMode(_transmitEnablePin, OUTPUT); //ArduinoPins::fastWriteDigital(_transmitEnablePin, 0); // transmitter initially off @@ -502,8 +505,9 @@ Modbus::Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_ //_modbusmaster.begin(baud); //DIAG(F("ModbusInit: %d %d"), _transmitEnablePin, _baud); // Add device to HAL device chain + serial.begin(baud, SERIAL_8N1); IODevice::addDevice(this); - + _modbusmaster.begin(baud, SERIAL_8N1); // Add bus to CMRIbus chain. _nextBus = _busList; _busList = this; @@ -516,7 +520,6 @@ Modbus::Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_ // process any response data received. // When the slot time has finished, move on to the next device. void Modbus::_loop(unsigned long currentMicros) { - _currentMicros = currentMicros; if (_currentNode == NULL) { @@ -532,16 +535,18 @@ void Modbus::_loop(unsigned long currentMicros) { uint8_t error; //error = _modbusmaster->writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); - DIAG(F("ModbusHR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); + DIAG(F("ModbusHR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); - //error = _modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, (char*) _currentNode->coils, _currentNode->getNumCoils()); + error = _modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, (char*) _currentNode->coils, _currentNode->getNumCoils()); DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); - error = _modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, (char*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); + //error = _modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, (char*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); //error = _modbusmaster->readInputRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); - DIAG(F("ModbusIR: %d %d %d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); + DIAG(F("ModbusIR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); + + _currentNode = _currentNode->getNext(); //delayUntil(_currentMicros + _cycleTime * 1000UL); } diff --git a/IO_Modbus.h b/IO_Modbus.h index d5a0bbcf..6e640538 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -199,10 +199,10 @@ class Modbusnode : public IODevice { if (checkNoOverlap(firstVpin, nPins)) new Modbusnode(firstVpin, nPins, busNo, nodeID, numCoils, numDiscreteInputs, numHoldingRegisters, numInputRegisters); } Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils, uint8_t numDiscreteInputs, uint8_t numHoldingRegisters, uint8_t numInputRegisters); - char *coils[100]; - char *discreteInputs[100]; - uint16_t *holdingRegisters[100]; - uint16_t *inputRegisters[100]; + char *coils[1968]; + char *discreteInputs[2000]; + uint16_t *holdingRegisters[123]; + uint16_t *inputRegisters[125]; uint8_t getNodeID() { return _nodeID; @@ -304,7 +304,6 @@ class Modbus : public IODevice { uint8_t _busNo; unsigned long _baud; - int8_t _transmitEnablePin; Modbusnode *_nodeListStart = NULL, *_nodeListEnd = NULL; Modbusnode *_currentNode = NULL; @@ -319,20 +318,21 @@ class Modbus : public IODevice { unsigned long _byteTransmitTime; // time in us for transmission of one byte static Modbus *_busList; // linked list of defined bus instances - + public: - static void create(uint8_t busNo, HardwareSerial& serial, unsigned long baud, uint16_t cycleTimeMS=500, int8_t transmitEnablePin=0) { - new Modbus(busNo, serial, baud, cycleTimeMS, transmitEnablePin); + static void create(uint8_t busNo, HardwareSerial& serial, unsigned long baud, uint16_t cycleTimeMS=500, int8_t _dePin=0) { + new Modbus(busNo, serial, baud, cycleTimeMS, _dePin); } - HardwareSerial *_serialD; + HardwareSerial *_serialD = nullptr; ModbusRTUMaster *_modbusmaster; - + // Device-specific initialisation void _begin() override { - ModbusRTUMaster _modbusmaster(*_serialD, _transmitEnablePin, -1); - _serialD->begin(_baud, SERIAL_8N1); + //ModbusRTUMaster _modbusmaster(*_serialD, _transmitEnablePin, -1); + + //_serialD->println("test"); - _modbusmaster.begin(_baud, SERIAL_8N1); + #if defined(DIAG_IO) _display(); #endif @@ -343,7 +343,7 @@ class Modbus : public IODevice { // Display information about the device void _display() override { - DIAG(F("Modbus Configured on %d Vpins:%d-%d %S"), _transmitEnablePin, _firstVpin, _firstVpin+_nPins-1, + DIAG(F("Modbus Configured on Vpins:%d-%d %S"), _firstVpin, _firstVpin+_nPins-1, _deviceState == DEVSTATE_FAILED ? F("OFFLINE") : F("OK")); } @@ -368,7 +368,7 @@ class Modbus : public IODevice { } protected: - Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_t cycleTimeMS, int8_t transmitEnablePin); + Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_t cycleTimeMS, int8_t _dePin); public: From d0bbdae7145f50f848d01175b4c18e4790ba2a91 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Wed, 27 Nov 2024 15:40:02 -0500 Subject: [PATCH 19/73] seems to work, but need to test on hardware --- IO_Modbus.cpp | 75 ++++++++++++++++++++++++--------------------------- IO_Modbus.h | 74 +++++++++++++++++++++++++------------------------- 2 files changed, 72 insertions(+), 77 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 7942bb7e..160236cc 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -171,7 +171,7 @@ uint16_t div8RndUp(uint16_t value) { return (value + 7) >> 3; } -ModbusRTUComm::ModbusRTUComm(Stream& serial, int8_t dePin, int8_t rePin) : _serial(serial) { +ModbusRTUComm::ModbusRTUComm(Stream& serial, VPIN dePin, VPIN rePin) : _serial(serial) { _dePin = dePin; _rePin = rePin; } @@ -204,13 +204,13 @@ void ModbusRTUComm::begin(unsigned long baud, uint32_t config) { #if defined(ARDUINO_UNOR4_MINIMA) || defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_GIGA) || (defined(ARDUINO_NANO_RP2040_CONNECT) && defined(ARDUINO_ARCH_MBED)) _postDelay = ((bitsPerChar * 1000000) / baud) + 2; #endif - if (_dePin >= 0) { + if (_dePin != VPIN_NONE) { pinMode(_dePin, OUTPUT); - digitalWrite(_dePin, LOW); + ArduinoPins::fastWriteDigital(_dePin, LOW); } - if (_rePin >= 0) { + if (_rePin != VPIN_NONE) { pinMode(_rePin, OUTPUT); - digitalWrite(_rePin, LOW); + ArduinoPins::fastWriteDigital(_rePin, LOW); } clearRxBuffer(); } @@ -249,13 +249,13 @@ ModbusRTUCommError ModbusRTUComm::readAdu(ModbusADU& adu) { void ModbusRTUComm::writeAdu(ModbusADU& adu) { adu.updateCrc(); - if (_dePin >= 0) digitalWrite(_dePin, HIGH); - if (_rePin >= 0) digitalWrite(_rePin, HIGH); + if (_dePin != VPIN_NONE) ArduinoPins::fastWriteDigital(_dePin, HIGH); + if (_rePin != VPIN_NONE) ArduinoPins::fastWriteDigital(_rePin, HIGH); _serial.write(adu.rtu, adu.getRtuLen()); _serial.flush(); - ///delayMicroseconds(_postDelay); - if (_dePin >= 0) digitalWrite(_dePin, LOW); - if (_rePin >= 0) digitalWrite(_rePin, LOW); + //delayMicroseconds(_postDelay); + if (_dePin != VPIN_NONE) ArduinoPins::fastWriteDigital(_dePin, LOW); + if (_rePin != VPIN_NONE) ArduinoPins::fastWriteDigital(_rePin, LOW); } void ModbusRTUComm::clearRxBuffer() { @@ -268,49 +268,43 @@ void ModbusRTUComm::clearRxBuffer() { } while (micros() - startMicros < _frameTimeout); } -ModbusRTUMaster::ModbusRTUMaster(Stream& serial, int8_t dePin, int8_t rePin) : _rtuComm(serial, dePin, rePin) { - _rtuComm.setTimeout(500); -} -void ModbusRTUMaster::setTimeout(unsigned long timeout) { +void Modbus::setTimeout(unsigned long timeout) { _rtuComm.setTimeout(timeout); } -void ModbusRTUMaster::begin(unsigned long baud, uint32_t config) { - _rtuComm.begin(baud, config); -} -ModbusRTUMasterError ModbusRTUMaster::readCoils(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity) { +ModbusRTUMasterError Modbus::readCoils(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity) { return _readValues(id, 1, startAddress, buf, quantity); } -ModbusRTUMasterError ModbusRTUMaster::readDiscreteInputs(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity) { +ModbusRTUMasterError Modbus::readDiscreteInputs(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity) { return _readValues(id, 2, startAddress, buf, quantity); } -ModbusRTUMasterError ModbusRTUMaster::readHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { +ModbusRTUMasterError Modbus::readHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { return _readValues(id, 3, startAddress, buf, quantity); } -ModbusRTUMasterError ModbusRTUMaster::readInputRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { +ModbusRTUMasterError Modbus::readInputRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { return _readValues(id, 4, startAddress, buf, quantity); } -ModbusRTUMasterError ModbusRTUMaster::writeSingleCoil(uint8_t id, uint16_t address, char value) { +ModbusRTUMasterError Modbus::writeSingleCoil(uint8_t id, uint16_t address, char value) { return _writeSingleValue(id, 5, address, ((value) ? 0xFF00 : 0x0000)); } -ModbusRTUMasterError ModbusRTUMaster::writeSingleHoldingRegister(uint8_t id, uint16_t address, uint16_t value) { +ModbusRTUMasterError Modbus::writeSingleHoldingRegister(uint8_t id, uint16_t address, uint16_t value) { return _writeSingleValue(id, 6, address, value); } -ModbusRTUMasterError ModbusRTUMaster::writeMultipleCoils(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity) { +ModbusRTUMasterError Modbus::writeMultipleCoils(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity) { const uint8_t functionCode = 15; if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; @@ -345,7 +339,7 @@ ModbusRTUMasterError ModbusRTUMaster::writeMultipleCoils(uint8_t id, uint16_t st return MODBUS_RTU_MASTER_SUCCESS; } -ModbusRTUMasterError ModbusRTUMaster::writeMultipleHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { +ModbusRTUMasterError Modbus::writeMultipleHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { uint8_t functionCode = 16; if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; @@ -379,13 +373,13 @@ ModbusRTUMasterError ModbusRTUMaster::writeMultipleHoldingRegisters(uint8_t id, -uint8_t ModbusRTUMaster::getExceptionResponse() { +uint8_t Modbus::getExceptionResponse() { return _exceptionResponse; } -ModbusRTUMasterError ModbusRTUMaster::_readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, char buf[], uint16_t quantity) { +ModbusRTUMasterError Modbus::_readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, char buf[], uint16_t quantity) { if (id < 1 || id > 247) return MODBUS_RTU_MASTER_INVALID_ID; if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; if (quantity == 0 || quantity > 2000) return MODBUS_RTU_MASTER_INVALID_QUANTITY; @@ -413,7 +407,7 @@ ModbusRTUMasterError ModbusRTUMaster::_readValues(uint8_t id, uint8_t functionCo return MODBUS_RTU_MASTER_SUCCESS; } -ModbusRTUMasterError ModbusRTUMaster::_readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { +ModbusRTUMasterError Modbus::_readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { if (id < 1 || id > 247) return MODBUS_RTU_MASTER_INVALID_ID; if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; if (quantity == 0 || quantity > 125) return MODBUS_RTU_MASTER_INVALID_QUANTITY; @@ -441,7 +435,7 @@ ModbusRTUMasterError ModbusRTUMaster::_readValues(uint8_t id, uint8_t functionCo return MODBUS_RTU_MASTER_SUCCESS; } -ModbusRTUMasterError ModbusRTUMaster::_writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value) { +ModbusRTUMasterError Modbus::_writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value) { if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; ModbusADU adu; adu.setUnitId(id); @@ -467,7 +461,7 @@ ModbusRTUMasterError ModbusRTUMaster::_writeSingleValue(uint8_t id, uint8_t func -ModbusRTUMasterError ModbusRTUMaster::_translateCommError(ModbusRTUCommError commError) { +ModbusRTUMasterError Modbus::_translateCommError(ModbusRTUCommError commError) { switch (commError) { case MODBUS_RTU_COMM_SUCCESS: return MODBUS_RTU_MASTER_SUCCESS; @@ -488,12 +482,13 @@ ModbusRTUMasterError ModbusRTUMaster::_translateCommError(ModbusRTUCommError com // Constructor for Modbus -Modbus::Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_t cycleTimeMS, int8_t _dePin) { - - //serial.write("\002\004",2); - ModbusRTUMaster _modbusmaster(serial, _dePin); +Modbus::Modbus(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin) : _rtuComm(serial, txPin) { _baud = baud; _serialD = &serial; + _txPin = txPin; + _rtuComm.setTimeout(500); + + _busNo = busNo; _cycleTime = cycleTimeMS * 1000UL; // convert from milliseconds to microseconds. //if (_transmitEnablePin != VPIN_NONE) { @@ -505,9 +500,9 @@ Modbus::Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_ //_modbusmaster.begin(baud); //DIAG(F("ModbusInit: %d %d"), _transmitEnablePin, _baud); // Add device to HAL device chain - serial.begin(baud, SERIAL_8N1); + IODevice::addDevice(this); - _modbusmaster.begin(baud, SERIAL_8N1); + // Add bus to CMRIbus chain. _nextBus = _busList; _busList = this; @@ -534,16 +529,16 @@ void Modbus::_loop(unsigned long currentMicros) { const char* errorStrings[16] = { "success", "invalid id", "invalid buffer", "invalid quantity", "response timeout", "frame error", "crc error", "unknown comm error", "unexpected id", "exception response", "unexpected function code", "unexpected response length", "unexpected byte count", "unexpected address", "unexpected value", "unexpected quantity" }; uint8_t error; - //error = _modbusmaster->writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); + error = writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); DIAG(F("ModbusHR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); - error = _modbusmaster->writeMultipleCoils(_currentNode->getNodeID(), 0, (char*) _currentNode->coils, _currentNode->getNumCoils()); + error = writeMultipleCoils(_currentNode->getNodeID(), 0, (char*) _currentNode->coils, _currentNode->getNumCoils()); DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); - //error = _modbusmaster->readDiscreteInputs(_currentNode->getNodeID(), 0, (char*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); + error = readDiscreteInputs(_currentNode->getNodeID(), 0, (char*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); - //error = _modbusmaster->readInputRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); + error = readInputRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); DIAG(F("ModbusIR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); diff --git a/IO_Modbus.h b/IO_Modbus.h index 6e640538..a8313eae 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -107,17 +107,17 @@ enum ModbusRTUCommError : uint8_t { class ModbusRTUComm { public: - ModbusRTUComm(Stream& serial, int8_t dePin = -1, int8_t rePin = -1); + ModbusRTUComm(Stream& serial, VPIN dePin = VPIN_NONE, VPIN rePin = VPIN_NONE); void begin(unsigned long baud, uint32_t config = SERIAL_8N1); void setTimeout(unsigned long timeout); ModbusRTUCommError readAdu(ModbusADU& adu); void writeAdu(ModbusADU& adu); void clearRxBuffer(); - - private: Stream& _serial; - int8_t _dePin; - int8_t _rePin; + VPIN _dePin; + VPIN _rePin; + private: + unsigned long _charTimeout; unsigned long _frameTimeout; unsigned long _postDelay = 0; @@ -145,32 +145,16 @@ enum ModbusRTUMasterError : uint8_t { class ModbusRTUMaster { public: - ModbusRTUMaster(Stream& serial, int8_t dePin = -1, int8_t rePin = -1); - void setTimeout(unsigned long timeout); + ModbusRTUMaster(Stream& serial, VPIN dePin = VPIN_NONE, VPIN rePin = VPIN_NONE); + void begin(unsigned long baud, uint32_t config = SERIAL_8N1); - ModbusRTUMasterError readCoils(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity); - ModbusRTUMasterError readDiscreteInputs(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity); - ModbusRTUMasterError readHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); - ModbusRTUMasterError readInputRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); - - ModbusRTUMasterError writeSingleCoil(uint8_t id, uint16_t address, char value); - ModbusRTUMasterError writeSingleHoldingRegister(uint8_t id, uint16_t address, uint16_t value); - ModbusRTUMasterError writeMultipleCoils(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity); - ModbusRTUMasterError writeMultipleHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); + - uint8_t getExceptionResponse(); + private: - ModbusRTUComm _rtuComm; - uint8_t _exceptionResponse = 0; - - ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, char buf[], uint16_t quantity); - ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity); - ModbusRTUMasterError _writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value); - - ModbusRTUMasterError _translateCommError(ModbusRTUCommError commError); - + }; @@ -304,11 +288,14 @@ class Modbus : public IODevice { uint8_t _busNo; unsigned long _baud; + int8_t _txPin; Modbusnode *_nodeListStart = NULL, *_nodeListEnd = NULL; Modbusnode *_currentNode = NULL; - + uint8_t _exceptionResponse = 0; + uint8_t getExceptionResponse(); uint16_t _receiveDataIndex = 0; // Index of next data byte to be received. Modbus *_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 @@ -318,26 +305,39 @@ class Modbus : public IODevice { unsigned long _byteTransmitTime; // time in us for transmission of one byte static Modbus *_busList; // linked list of defined bus instances - + ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, char buf[], uint16_t quantity); + ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity); + ModbusRTUMasterError _writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value); public: - static void create(uint8_t busNo, HardwareSerial& serial, unsigned long baud, uint16_t cycleTimeMS=500, int8_t _dePin=0) { - new Modbus(busNo, serial, baud, cycleTimeMS, _dePin); + static void create(uint8_t busNo, HardwareSerial& serial, unsigned long baud, uint16_t cycleTimeMS=500, int8_t txPin=-1) { + new Modbus(busNo, serial, baud, cycleTimeMS, txPin); } - HardwareSerial *_serialD = nullptr; - ModbusRTUMaster *_modbusmaster; - + HardwareSerial *_serialD; + //ModbusRTUMaster *_modbusmaster; + ModbusRTUComm _rtuComm; // Device-specific initialisation void _begin() override { //ModbusRTUMaster _modbusmaster(*_serialD, _transmitEnablePin, -1); - - + _serialD->begin(_baud, SERIAL_8N1); + //ModbusRTUMaster _modbusmaster(*_serialD, _txPin, -1); + _rtuComm.begin(_baud, SERIAL_8N1); + //_serialD->println("test"); #if defined(DIAG_IO) _display(); #endif } - + ModbusRTUMasterError _translateCommError(ModbusRTUCommError commError); + ModbusRTUMasterError readCoils(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity); + ModbusRTUMasterError readDiscreteInputs(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity); + ModbusRTUMasterError readHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); + ModbusRTUMasterError readInputRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); + + ModbusRTUMasterError writeSingleCoil(uint8_t id, uint16_t address, char value); + ModbusRTUMasterError writeSingleHoldingRegister(uint8_t id, uint16_t address, uint16_t value); + ModbusRTUMasterError writeMultipleCoils(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity); + ModbusRTUMasterError writeMultipleHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); // Loop function (overriding IODevice::_loop(unsigned long)) void _loop(unsigned long currentMicros) override; @@ -368,7 +368,7 @@ class Modbus : public IODevice { } protected: - Modbus(uint8_t busNo, HardwareSerial serial, unsigned long baud, uint16_t cycleTimeMS, int8_t _dePin); + Modbus(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin); public: From 6df1774f17bb595fd04699ed7b2c0cf08651a0f8 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Thu, 28 Nov 2024 04:17:30 -0500 Subject: [PATCH 20/73] cleaning up IO_Modbus --- IO_Modbus.cpp | 43 ++++++++++++++----------------- IO_Modbus.h | 70 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 68 insertions(+), 45 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 160236cc..8127c0ea 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -487,32 +487,19 @@ Modbus::Modbus(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint16 _serialD = &serial; _txPin = txPin; _rtuComm.setTimeout(500); - - _busNo = busNo; _cycleTime = cycleTimeMS * 1000UL; // convert from milliseconds to microseconds. - //if (_transmitEnablePin != VPIN_NONE) { - //pinMode(_transmitEnablePin, OUTPUT); - //ArduinoPins::fastWriteDigital(_transmitEnablePin, 0); // transmitter initially off - //} - //_serial->begin(baud); - //_modbusmaster.begin(baud); - //DIAG(F("ModbusInit: %d %d"), _transmitEnablePin, _baud); // Add device to HAL device chain - IODevice::addDevice(this); - // Add bus to CMRIbus chain. + // Add bus to Modbus chain. _nextBus = _busList; _busList = this; } // Main loop function for Modbus. // Work through list of nodes. For each node, in separate loop entries -// send initialisation message (once only); then send -// output message; then send prompt for input data, and -// process any response data received. // When the slot time has finished, move on to the next device. void Modbus::_loop(unsigned long currentMicros) { _currentMicros = currentMicros; @@ -521,7 +508,7 @@ void Modbus::_loop(unsigned long currentMicros) { _currentNode = _nodeListStart; } - //DIAG(F("Modbus Loop: %d : %d :: %d"), _currentMicros, _cycleStartTime, _currentNode); + if (_currentMicros - _cycleStartTime < _cycleTime) return; _cycleStartTime = _currentMicros; if (_currentNode == NULL) return; @@ -529,21 +516,29 @@ void Modbus::_loop(unsigned long currentMicros) { const char* errorStrings[16] = { "success", "invalid id", "invalid buffer", "invalid quantity", "response timeout", "frame error", "crc error", "unknown comm error", "unexpected id", "exception response", "unexpected function code", "unexpected response length", "unexpected byte count", "unexpected address", "unexpected value", "unexpected quantity" }; uint8_t error; + // send reads and writes, DIAG on errors other than 0 (Success), or 3 (Invalid Quantity) error = writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); - DIAG(F("ModbusHR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); - + if (error != 0 || error != 3) DIAG(F("ModbusHR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); +#ifdef DIAG_IO + if (error == 0) DIAG(F("ModbusHR: T%d Success!"), _currentNode->getNodeID()); +#endif error = writeMultipleCoils(_currentNode->getNodeID(), 0, (char*) _currentNode->coils, _currentNode->getNumCoils()); - DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); - + if (error != 0 || error != 3) DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); +#ifdef DIAG_IO + if (error == 0) DIAG(F("ModbusMC: T%d Success!"), _currentNode->getNodeID()); +#endif error = readDiscreteInputs(_currentNode->getNodeID(), 0, (char*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); - DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); - + if (error != 0 || error != 3) DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); +#ifdef DIAG_IO + if (error == 0) DIAG(F("ModbusDI: T%d Success!"), _currentNode->getNodeID()); +#endif error = readInputRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); - DIAG(F("ModbusIR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); - + if (error != 0 || error != 3) DIAG(F("ModbusIR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); +#ifdef DIAG_IO + if (error == 0) DIAG(F("ModbusIR: T%d Success!"), _currentNode->getNodeID()); +#endif _currentNode = _currentNode->getNext(); - //delayUntil(_currentMicros + _cycleTime * 1000UL); } // Link to chain of Modbus instances diff --git a/IO_Modbus.h b/IO_Modbus.h index a8313eae..4b0b5640 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -229,15 +229,10 @@ class Modbusnode : public IODevice { return 0; } - int _readAnalogue(VPIN vpin) override { - // Return acquired data value, e.g. - int pin = vpin - _firstVpin - _numDiscreteInputs; - return (int) inputRegisters[pin]; - } - + void _write(VPIN vpin, int value) override { // Update current state for this device, in preparation the bus transmission - uint16_t pin = vpin - _firstVpin - _numDiscreteInputs - _numInputRegisters; + uint16_t pin = vpin - _firstVpin - _numDiscreteInputs; if (pin < _numCoils) { if (value) if (value == 1) coils[pin] = (char*) 0x1; @@ -248,8 +243,14 @@ class Modbusnode : public IODevice { } } + int _readAnalogue(VPIN vpin) override { + // Return acquired data value, e.g. + int pin = vpin - _firstVpin - _numDiscreteInputs - _numCoils; + return (int) inputRegisters[pin]; + } + void _writeAnalogue(VPIN vpin, int value, uint8_t param1=0, uint16_t param2=0) override { - uint16_t pin = vpin - _firstVpin - _numDiscreteInputs - _numInputRegisters - _numCoils; + uint16_t pin = vpin - _firstVpin - _numDiscreteInputs - _numCoils - _numInputRegisters; if (pin < _numHoldingRegisters) { if (value) holdingRegisters[pin] = (uint16_t*) value; @@ -258,18 +259,50 @@ class Modbusnode : public IODevice { } } - char getType() { - return _type; - } - uint8_t getBusNumber() { return _busNo; } + uint8_t getNumBinaryInputsVPINsMin() { + if (_numDiscreteInputs > 0) return _firstVpin; + else return 0; + } + uint8_t getNumBinaryInputsVPINsMax() { + if (_numDiscreteInputs > 0) return _firstVpin+_numDiscreteInputs-1; + else return 0; + } + + uint8_t getNumBinaryOutputsVPINsMin() { + if (_numCoils > 0) return _firstVpin+_numDiscreteInputs; + else return 0; + } + uint8_t getNumBinaryOutputsVPINsMax() { + if (_numCoils > 0) return _firstVpin+_numDiscreteInputs+_numCoils-1; + else return 0; + } + + uint8_t getNumAnalogInputsVPINsMin() { + if (_numInputRegisters > 0) return _firstVpin+_numDiscreteInputs+_numCoils; + else return 0; + } + uint8_t getNumAnalogInputsVPINsMax() { + if (_numInputRegisters > 0) return _firstVpin+_numDiscreteInputs+_numCoils+_numInputRegisters-1; + else return 0; + } + uint8_t getNumAnalogOutputsVPINsMin() { + if (_numHoldingRegisters > 0) return _firstVpin+_numDiscreteInputs+_numCoils+_numInputRegisters; + else return 0; + } + uint8_t getNumAnalogOutputsVPINsMax() { + if (_numHoldingRegisters > 0) return _firstVpin+_numDiscreteInputs+_numCoils+_numInputRegisters+_numHoldingRegisters-1; + else return 0; + } void _display() override { - DIAG(F("Modbusnode configured on bus:%d nodeID:%d VPINs:%u-%u (in) %u-%u (out)"), - _busNo, _nodeID, _firstVpin, _firstVpin+_numDiscreteInputs-1, - _firstVpin+_numDiscreteInputs, _firstVpin+_numDiscreteInputs+_numCoils-1); + DIAG(F("Modbusnode configured on bus:%d nodeID:%d VPINs:%u-%u (B In) %u-%u (B Out) %u-%u (A In) %u-%u (A Out)"), + _busNo, _nodeID, getNumBinaryInputsVPINsMin(), getNumBinaryInputsVPINsMax(), + getNumBinaryOutputsVPINsMin(), getNumBinaryOutputsVPINsMax(), + getNumAnalogInputsVPINsMin(), getNumAnalogInputsVPINsMax(), + getNumAnalogOutputsVPINsMin(), getNumAnalogOutputsVPINsMax()); } }; @@ -313,17 +346,12 @@ class Modbus : public IODevice { new Modbus(busNo, serial, baud, cycleTimeMS, txPin); } HardwareSerial *_serialD; - //ModbusRTUMaster *_modbusmaster; ModbusRTUComm _rtuComm; // Device-specific initialisation void _begin() override { - //ModbusRTUMaster _modbusmaster(*_serialD, _transmitEnablePin, -1); _serialD->begin(_baud, SERIAL_8N1); - //ModbusRTUMaster _modbusmaster(*_serialD, _txPin, -1); _rtuComm.begin(_baud, SERIAL_8N1); - - //_serialD->println("test"); - + #if defined(DIAG_IO) _display(); #endif From 5d219044f22ab74c9e336f4e0f17c1ce683f26ac Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Thu, 28 Nov 2024 04:20:22 -0500 Subject: [PATCH 21/73] tidy up IO_Modbus some more --- IO_Modbus.h | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/IO_Modbus.h b/IO_Modbus.h index 4b0b5640..40d46747 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -143,21 +143,6 @@ enum ModbusRTUMasterError : uint8_t { MODBUS_RTU_MASTER_UNEXPECTED_QUANTITY = 15 }; -class ModbusRTUMaster { - public: - ModbusRTUMaster(Stream& serial, VPIN dePin = VPIN_NONE, VPIN rePin = VPIN_NONE); - - void begin(unsigned long baud, uint32_t config = SERIAL_8N1); - - - - - - private: - -}; - - /********************************************************************** * Modbusnode class From ad7045634102c16c4ee5d68919a7f0e3df11125d Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Thu, 28 Nov 2024 05:22:11 -0500 Subject: [PATCH 22/73] IO_Modbus: add status LEDs, STM32 --- IO_Modbus.cpp | 29 ++++++++++++++++++++++++----- IO_Modbus.h | 9 ++++++++- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 8127c0ea..bbd6fdf5 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -515,29 +515,48 @@ void Modbus::_loop(unsigned long currentMicros) { const char* errorStrings[16] = { "success", "invalid id", "invalid buffer", "invalid quantity", "response timeout", "frame error", "crc error", "unknown comm error", "unexpected id", "exception response", "unexpected function code", "unexpected response length", "unexpected byte count", "unexpected address", "unexpected value", "unexpected quantity" }; + bool flagOK = false; +#if defined(MODBUS_STM_OK) && defined(MODBUS_STM_FAIL) && defined(MODBUS_STM_TICK) + ArduinoPins::fastWriteDigital(MODBUS_STM_TICK,HIGH); +#endif + uint8_t error; // send reads and writes, DIAG on errors other than 0 (Success), or 3 (Invalid Quantity) error = writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); - if (error != 0 || error != 3) DIAG(F("ModbusHR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); + if (error != 0 && error != 3) DIAG(F("ModbusHR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); #ifdef DIAG_IO if (error == 0) DIAG(F("ModbusHR: T%d Success!"), _currentNode->getNodeID()); #endif + if (error == 0 || error == 3) flagOK = true; error = writeMultipleCoils(_currentNode->getNodeID(), 0, (char*) _currentNode->coils, _currentNode->getNumCoils()); - if (error != 0 || error != 3) DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); + if (error != 0 && error != 3) DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); #ifdef DIAG_IO if (error == 0) DIAG(F("ModbusMC: T%d Success!"), _currentNode->getNodeID()); #endif + if (error == 0 || error == 3) flagOK = true; error = readDiscreteInputs(_currentNode->getNodeID(), 0, (char*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); - if (error != 0 || error != 3) DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); + if (error != 0 && error != 3) DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); #ifdef DIAG_IO if (error == 0) DIAG(F("ModbusDI: T%d Success!"), _currentNode->getNodeID()); #endif + if (error == 0 || error == 3) flagOK = true; error = readInputRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); - if (error != 0 || error != 3) DIAG(F("ModbusIR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); + if (error != 0 && error != 3) DIAG(F("ModbusIR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); #ifdef DIAG_IO if (error == 0) DIAG(F("ModbusIR: T%d Success!"), _currentNode->getNodeID()); #endif - + if (error == 0 || error == 3) flagOK = true; + +#if defined(MODBUS_STM_OK) && defined(MODBUS_STM_FAIL) && defined(MODBUS_STM_TICK) + if (flagOK == true) { + ArduinoPins::fastWriteDigital(MODBUS_STM_OK,HIGH); + ArduinoPins::fastWriteDigital(MODBUS_STM_FAIL,LOW); + } else { + ArduinoPins::fastWriteDigital(MODBUS_STM_OK,LOW); + ArduinoPins::fastWriteDigital(MODBUS_STM_FAIL,HIGH); + } + ArduinoPins::fastWriteDigital(MODBUS_STM_TICK,LOW); +#endif _currentNode = _currentNode->getNext(); } diff --git a/IO_Modbus.h b/IO_Modbus.h index 40d46747..786c92be 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -336,7 +336,14 @@ class Modbus : public IODevice { void _begin() override { _serialD->begin(_baud, SERIAL_8N1); _rtuComm.begin(_baud, SERIAL_8N1); - + #if defined(MODBUS_STM_OK) && defined(MODBUS_STM_FAIL) && defined(MODBUS_STM_TICK) + pinMode(MODBUS_STM_OK, OUTPUT); + pinMode(MODBUS_STM_FAIL, OUTPUT); + pinMode(MODBUS_STM_TICK, OUTPUT); + ArduinoPins::fastWriteDigital(MODBUS_STM_OK,LOW); + ArduinoPins::fastWriteDigital(MODBUS_STM_FAIL,LOW); + ArduinoPins::fastWriteDigital(MODBUS_STM_TICK,LOW); + #endif #if defined(DIAG_IO) _display(); #endif From 6551ce7b50959cd30c269e78d06e2b4db6574755 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Thu, 28 Nov 2024 07:36:12 -0500 Subject: [PATCH 23/73] hardware test shows VPINs are off by one --- IO_Modbus.cpp | 30 ++++++++++++++++++------------ IO_Modbus.h | 22 ++++++++++++++-------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index bbd6fdf5..ad065e4a 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -253,7 +253,7 @@ void ModbusRTUComm::writeAdu(ModbusADU& adu) { if (_rePin != VPIN_NONE) ArduinoPins::fastWriteDigital(_rePin, HIGH); _serial.write(adu.rtu, adu.getRtuLen()); _serial.flush(); - //delayMicroseconds(_postDelay); + delayMicroseconds(_postDelay); if (_dePin != VPIN_NONE) ArduinoPins::fastWriteDigital(_dePin, LOW); if (_rePin != VPIN_NONE) ArduinoPins::fastWriteDigital(_rePin, LOW); } @@ -515,11 +515,10 @@ void Modbus::_loop(unsigned long currentMicros) { const char* errorStrings[16] = { "success", "invalid id", "invalid buffer", "invalid quantity", "response timeout", "frame error", "crc error", "unknown comm error", "unexpected id", "exception response", "unexpected function code", "unexpected response length", "unexpected byte count", "unexpected address", "unexpected value", "unexpected quantity" }; - bool flagOK = false; -#if defined(MODBUS_STM_OK) && defined(MODBUS_STM_FAIL) && defined(MODBUS_STM_TICK) - ArduinoPins::fastWriteDigital(MODBUS_STM_TICK,HIGH); + bool flagOK = true; +#if defined(MODBUS_STM_COMM) + ArduinoPins::fastWriteDigital(MODBUS_STM_COMM,HIGH); #endif - uint8_t error; // send reads and writes, DIAG on errors other than 0 (Success), or 3 (Invalid Quantity) error = writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); @@ -527,35 +526,42 @@ void Modbus::_loop(unsigned long currentMicros) { #ifdef DIAG_IO if (error == 0) DIAG(F("ModbusHR: T%d Success!"), _currentNode->getNodeID()); #endif - if (error == 0 || error == 3) flagOK = true; + if (error != 0 && error != 3) flagOK = false; error = writeMultipleCoils(_currentNode->getNodeID(), 0, (char*) _currentNode->coils, _currentNode->getNumCoils()); if (error != 0 && error != 3) DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); #ifdef DIAG_IO if (error == 0) DIAG(F("ModbusMC: T%d Success!"), _currentNode->getNodeID()); #endif - if (error == 0 || error == 3) flagOK = true; + if (error != 0 && error != 3) flagOK = false; error = readDiscreteInputs(_currentNode->getNodeID(), 0, (char*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); if (error != 0 && error != 3) DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); #ifdef DIAG_IO if (error == 0) DIAG(F("ModbusDI: T%d Success!"), _currentNode->getNodeID()); #endif - if (error == 0 || error == 3) flagOK = true; + if (error != 0 && error != 3) flagOK = false; error = readInputRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); if (error != 0 && error != 3) DIAG(F("ModbusIR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); #ifdef DIAG_IO if (error == 0) DIAG(F("ModbusIR: T%d Success!"), _currentNode->getNodeID()); #endif - if (error == 0 || error == 3) flagOK = true; + if (error != 0 && error != 3) flagOK = false; -#if defined(MODBUS_STM_OK) && defined(MODBUS_STM_FAIL) && defined(MODBUS_STM_TICK) +#if defined(MODBUS_STM_OK) if (flagOK == true) { ArduinoPins::fastWriteDigital(MODBUS_STM_OK,HIGH); - ArduinoPins::fastWriteDigital(MODBUS_STM_FAIL,LOW); } else { ArduinoPins::fastWriteDigital(MODBUS_STM_OK,LOW); + } +#endif +#if defined(MODBUS_STM_FAIL) + if (flagOK == false) { ArduinoPins::fastWriteDigital(MODBUS_STM_FAIL,HIGH); + } else { + ArduinoPins::fastWriteDigital(MODBUS_STM_FAIL,LOW); } - ArduinoPins::fastWriteDigital(MODBUS_STM_TICK,LOW); +#endif +#if defined(MODBUS_STM_COMM) + ArduinoPins::fastWriteDigital(MODBUS_STM_COMM,LOW); #endif _currentNode = _currentNode->getNext(); } diff --git a/IO_Modbus.h b/IO_Modbus.h index 786c92be..01b87e6a 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -219,10 +219,11 @@ class Modbusnode : public IODevice { // Update current state for this device, in preparation the bus transmission uint16_t pin = vpin - _firstVpin - _numDiscreteInputs; if (pin < _numCoils) { - if (value) + if (value){ if (value == 1) coils[pin] = (char*) 0x1; if (value == 0) coils[pin] = (char*) 0x0; //coils[pin] = value; + } else coils[pin]; } @@ -231,16 +232,16 @@ class Modbusnode : public IODevice { int _readAnalogue(VPIN vpin) override { // Return acquired data value, e.g. int pin = vpin - _firstVpin - _numDiscreteInputs - _numCoils; - return (int) inputRegisters[pin]; + return (int) inputRegisters[pin-1]; } void _writeAnalogue(VPIN vpin, int value, uint8_t param1=0, uint16_t param2=0) override { uint16_t pin = vpin - _firstVpin - _numDiscreteInputs - _numCoils - _numInputRegisters; if (pin < _numHoldingRegisters) { if (value) - holdingRegisters[pin] = (uint16_t*) value; + holdingRegisters[pin-1] = (uint16_t*) value; else - holdingRegisters[pin]; + holdingRegisters[pin-1]; } } @@ -336,14 +337,19 @@ class Modbus : public IODevice { void _begin() override { _serialD->begin(_baud, SERIAL_8N1); _rtuComm.begin(_baud, SERIAL_8N1); - #if defined(MODBUS_STM_OK) && defined(MODBUS_STM_FAIL) && defined(MODBUS_STM_TICK) + #if defined(MODBUS_STM_OK) pinMode(MODBUS_STM_OK, OUTPUT); - pinMode(MODBUS_STM_FAIL, OUTPUT); - pinMode(MODBUS_STM_TICK, OUTPUT); ArduinoPins::fastWriteDigital(MODBUS_STM_OK,LOW); + #endif + #if defined(MODBUS_STM_FAIL) + pinMode(MODBUS_STM_FAIL, OUTPUT); ArduinoPins::fastWriteDigital(MODBUS_STM_FAIL,LOW); - ArduinoPins::fastWriteDigital(MODBUS_STM_TICK,LOW); #endif + #if defined(MODBUS_STM_COMM) + pinMode(MODBUS_STM_COMM, OUTPUT); + ArduinoPins::fastWriteDigital(MODBUS_STM_COMM,LOW); + #endif + #if defined(DIAG_IO) _display(); #endif From 367b86cd09261dc119090d52897c81a051eaa815 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Thu, 28 Nov 2024 08:06:01 -0500 Subject: [PATCH 24/73] Maybe fix? --- IO_Modbus.cpp | 2 +- IO_Modbus.h | 27 ++++++--------------------- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index ad065e4a..17e0878a 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -317,7 +317,7 @@ ModbusRTUMasterError Modbus::writeMultipleCoils(uint8_t id, uint16_t startAddres adu.setDataRegister(2, quantity); adu.data[4] = byteCount; for (uint16_t i = 0; i < quantity; i++) { - bitWrite(adu.data[5 + (i >> 3)], i & 7, (bool) buf[i]); + bitWrite(adu.data[5 + (i >> 3)], i & 7, buf[i]); } for (uint16_t i = quantity; i < (byteCount * 8); i++) { bitClear(adu.data[5 + (i >> 3)], i & 7); diff --git a/IO_Modbus.h b/IO_Modbus.h index 01b87e6a..55fba170 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -208,41 +208,26 @@ class Modbusnode : public IODevice { int _read(VPIN vpin) override { // Return current state from this device uint16_t pin = vpin - _firstVpin; - if (pin < _numDiscreteInputs) { - return (int) discreteInputs[pin]; - } else - return 0; + return (int) discreteInputs[pin]; } void _write(VPIN vpin, int value) override { // Update current state for this device, in preparation the bus transmission uint16_t pin = vpin - _firstVpin - _numDiscreteInputs; - if (pin < _numCoils) { - if (value){ - if (value == 1) coils[pin] = (char*) 0x1; - if (value == 0) coils[pin] = (char*) 0x0; - //coils[pin] = value; - } - else - coils[pin]; - } + if (value == 1) coils[pin] = (char*) 0x1; + if (value == 0) coils[pin] = (char*) 0x0; } - int _readAnalogue(VPIN vpin) override { + int _readAnalogue(VPIN vpin) { // Return acquired data value, e.g. int pin = vpin - _firstVpin - _numDiscreteInputs - _numCoils; - return (int) inputRegisters[pin-1]; + return (int) inputRegisters[pin]; } void _writeAnalogue(VPIN vpin, int value, uint8_t param1=0, uint16_t param2=0) override { uint16_t pin = vpin - _firstVpin - _numDiscreteInputs - _numCoils - _numInputRegisters; - if (pin < _numHoldingRegisters) { - if (value) - holdingRegisters[pin-1] = (uint16_t*) value; - else - holdingRegisters[pin-1]; - } + holdingRegisters[pin] = (uint16_t*) value; } uint8_t getBusNumber() { From 411d5c0087ab32efe00d1b2433ba9dc73fc0c3c6 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Thu, 28 Nov 2024 16:59:13 -0500 Subject: [PATCH 25/73] IO_Modbus: Works! pending further tests... --- IO_Modbus.cpp | 16 ++++++++-------- IO_Modbus.h | 22 +++++++++++----------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 17e0878a..6ff0ce07 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -276,11 +276,11 @@ void Modbus::setTimeout(unsigned long timeout) { -ModbusRTUMasterError Modbus::readCoils(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity) { +ModbusRTUMasterError Modbus::readCoils(uint8_t id, uint16_t startAddress, int buf[], uint16_t quantity) { return _readValues(id, 1, startAddress, buf, quantity); } -ModbusRTUMasterError Modbus::readDiscreteInputs(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity) { +ModbusRTUMasterError Modbus::readDiscreteInputs(uint8_t id, uint16_t startAddress, int buf[], uint16_t quantity) { return _readValues(id, 2, startAddress, buf, quantity); } @@ -294,7 +294,7 @@ ModbusRTUMasterError Modbus::readInputRegisters(uint8_t id, uint16_t startAddres -ModbusRTUMasterError Modbus::writeSingleCoil(uint8_t id, uint16_t address, char value) { +ModbusRTUMasterError Modbus::writeSingleCoil(uint8_t id, uint16_t address, int value) { return _writeSingleValue(id, 5, address, ((value) ? 0xFF00 : 0x0000)); } @@ -304,7 +304,7 @@ ModbusRTUMasterError Modbus::writeSingleHoldingRegister(uint8_t id, uint16_t add -ModbusRTUMasterError Modbus::writeMultipleCoils(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity) { +ModbusRTUMasterError Modbus::writeMultipleCoils(uint8_t id, uint16_t startAddress, int buf[], uint16_t quantity) { const uint8_t functionCode = 15; if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; @@ -379,7 +379,7 @@ uint8_t Modbus::getExceptionResponse() { -ModbusRTUMasterError Modbus::_readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, char buf[], uint16_t quantity) { +ModbusRTUMasterError Modbus::_readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, int buf[], uint16_t quantity) { if (id < 1 || id > 247) return MODBUS_RTU_MASTER_INVALID_ID; if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; if (quantity == 0 || quantity > 2000) return MODBUS_RTU_MASTER_INVALID_QUANTITY; @@ -402,7 +402,7 @@ ModbusRTUMasterError Modbus::_readValues(uint8_t id, uint8_t functionCode, uint1 if (adu.getDataLen() != (1 + byteCount)) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; if (adu.data[0] != byteCount) return MODBUS_RTU_MASTER_UNEXPECTED_BYTE_COUNT; for (uint16_t i = 0; i < quantity; i++) { - buf[i] = (char) bitRead(adu.data[1 + (i >> 3)], i & 7); + buf[i] = (int) bitRead(adu.data[1 + (i >> 3)], i & 7)? 1:0; } return MODBUS_RTU_MASTER_SUCCESS; } @@ -527,13 +527,13 @@ void Modbus::_loop(unsigned long currentMicros) { if (error == 0) DIAG(F("ModbusHR: T%d Success!"), _currentNode->getNodeID()); #endif if (error != 0 && error != 3) flagOK = false; - error = writeMultipleCoils(_currentNode->getNodeID(), 0, (char*) _currentNode->coils, _currentNode->getNumCoils()); + error = writeMultipleCoils(_currentNode->getNodeID(), 0, (int*) _currentNode->coils, _currentNode->getNumCoils()); if (error != 0 && error != 3) DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); #ifdef DIAG_IO if (error == 0) DIAG(F("ModbusMC: T%d Success!"), _currentNode->getNodeID()); #endif if (error != 0 && error != 3) flagOK = false; - error = readDiscreteInputs(_currentNode->getNodeID(), 0, (char*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); + error = readDiscreteInputs(_currentNode->getNodeID(), 0, (int*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); if (error != 0 && error != 3) DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); #ifdef DIAG_IO if (error == 0) DIAG(F("ModbusDI: T%d Success!"), _currentNode->getNodeID()); diff --git a/IO_Modbus.h b/IO_Modbus.h index 55fba170..55398086 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -168,10 +168,10 @@ class Modbusnode : public IODevice { if (checkNoOverlap(firstVpin, nPins)) new Modbusnode(firstVpin, nPins, busNo, nodeID, numCoils, numDiscreteInputs, numHoldingRegisters, numInputRegisters); } Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils, uint8_t numDiscreteInputs, uint8_t numHoldingRegisters, uint8_t numInputRegisters); - char *coils[1968]; - char *discreteInputs[2000]; - uint16_t *holdingRegisters[123]; - uint16_t *inputRegisters[125]; + int *coils[100]; + int *discreteInputs[100]; + uint16_t *holdingRegisters[100]; + uint16_t *inputRegisters[100]; uint8_t getNodeID() { return _nodeID; @@ -215,8 +215,8 @@ class Modbusnode : public IODevice { void _write(VPIN vpin, int value) override { // Update current state for this device, in preparation the bus transmission uint16_t pin = vpin - _firstVpin - _numDiscreteInputs; - if (value == 1) coils[pin] = (char*) 0x1; - if (value == 0) coils[pin] = (char*) 0x0; + if (value == 1) coils[pin] = (int*) 0x1; + if (value == 0) coils[pin] = (int*) 0x0; } int _readAnalogue(VPIN vpin) { @@ -309,7 +309,7 @@ class Modbus : public IODevice { unsigned long _byteTransmitTime; // time in us for transmission of one byte static Modbus *_busList; // linked list of defined bus instances - ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, char buf[], uint16_t quantity); + ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, int buf[], uint16_t quantity); ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity); ModbusRTUMasterError _writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value); public: @@ -340,14 +340,14 @@ class Modbus : public IODevice { #endif } ModbusRTUMasterError _translateCommError(ModbusRTUCommError commError); - ModbusRTUMasterError readCoils(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity); - ModbusRTUMasterError readDiscreteInputs(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity); + ModbusRTUMasterError readCoils(uint8_t id, uint16_t startAddress, int buf[], uint16_t quantity); + ModbusRTUMasterError readDiscreteInputs(uint8_t id, uint16_t startAddress, int buf[], uint16_t quantity); ModbusRTUMasterError readHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); ModbusRTUMasterError readInputRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); - ModbusRTUMasterError writeSingleCoil(uint8_t id, uint16_t address, char value); + ModbusRTUMasterError writeSingleCoil(uint8_t id, uint16_t address, int value); ModbusRTUMasterError writeSingleHoldingRegister(uint8_t id, uint16_t address, uint16_t value); - ModbusRTUMasterError writeMultipleCoils(uint8_t id, uint16_t startAddress, char buf[], uint16_t quantity); + ModbusRTUMasterError writeMultipleCoils(uint8_t id, uint16_t startAddress, int buf[], uint16_t quantity); ModbusRTUMasterError writeMultipleHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); // Loop function (overriding IODevice::_loop(unsigned long)) void _loop(unsigned long currentMicros) override; From 9e5b86405529cdf4218d5929ccc018c4062de541 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Fri, 29 Nov 2024 04:38:57 -0500 Subject: [PATCH 26/73] IO_Modbus: commented out Mbed delay --- IO_Modbus.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 6ff0ce07..2a519553 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -253,7 +253,7 @@ void ModbusRTUComm::writeAdu(ModbusADU& adu) { if (_rePin != VPIN_NONE) ArduinoPins::fastWriteDigital(_rePin, HIGH); _serial.write(adu.rtu, adu.getRtuLen()); _serial.flush(); - delayMicroseconds(_postDelay); + ///delayMicroseconds(_postDelay); // TJF: Commented out as Mbed platforms are not supported if (_dePin != VPIN_NONE) ArduinoPins::fastWriteDigital(_dePin, LOW); if (_rePin != VPIN_NONE) ArduinoPins::fastWriteDigital(_rePin, LOW); } From a85fa6d9c280951be862808da643fbb735b68723 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sat, 30 Nov 2024 05:41:29 -0500 Subject: [PATCH 27/73] Remove blocking behaviour? --- IO_Modbus.cpp | 81 ++++++++++++++++++++++++++++++--------------------- IO_Modbus.h | 9 ++++-- 2 files changed, 54 insertions(+), 36 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 2a519553..887bf44a 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -222,9 +222,17 @@ void ModbusRTUComm::setTimeout(unsigned long timeout) { ModbusRTUCommError ModbusRTUComm::readAdu(ModbusADU& adu) { adu.setRtuLen(0); unsigned long startMillis = millis(); - while (!_serial.available()) { - if (millis() - startMillis >= _readTimeout) return MODBUS_RTU_COMM_TIMEOUT; + if (!_serial.available()) { + //if (millis() - startMillis >= _readTimeout) return MODBUS_RTU_COMM_TIMEOUT; + _waiting_for_read = true; + if (millis() - startMillis >= _readTimeout) { + return MODBUS_RTU_COMM_TIMEOUT; + } else { + return MODBUS_RTU_COMM_WAITING; + } + } + _waiting_for_read = false; uint16_t len = 0; unsigned long startMicros = micros(); do { @@ -323,7 +331,7 @@ ModbusRTUMasterError Modbus::writeMultipleCoils(uint8_t id, uint16_t startAddres bitClear(adu.data[5 + (i >> 3)], i & 7); } adu.setDataLen(5 + byteCount); - _rtuComm.writeAdu(adu); + if (_rtuComm._waiting_for_read == false) _rtuComm.writeAdu(adu); if (id == 0) return MODBUS_RTU_MASTER_SUCCESS; ModbusRTUCommError commError = _rtuComm.readAdu(adu); if (commError) return _translateCommError(commError); @@ -355,7 +363,7 @@ ModbusRTUMasterError Modbus::writeMultipleHoldingRegisters(uint8_t id, uint16_t adu.setDataRegister(5 + (i * 2), buf[i]); } adu.setDataLen(5 + byteCount); - _rtuComm.writeAdu(adu); + if (_rtuComm._waiting_for_read == false) _rtuComm.writeAdu(adu); if (id == 0) return MODBUS_RTU_MASTER_SUCCESS; ModbusRTUCommError commError = _rtuComm.readAdu(adu); if (commError) return _translateCommError(commError); @@ -389,7 +397,7 @@ ModbusRTUMasterError Modbus::_readValues(uint8_t id, uint8_t functionCode, uint1 adu.setDataRegister(0, startAddress); adu.setDataRegister(2, quantity); adu.setDataLen(4); - _rtuComm.writeAdu(adu); + if (_rtuComm._waiting_for_read == false) _rtuComm.writeAdu(adu); ModbusRTUCommError commError = _rtuComm.readAdu(adu); if (commError) return _translateCommError(commError); if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; @@ -417,7 +425,7 @@ ModbusRTUMasterError Modbus::_readValues(uint8_t id, uint8_t functionCode, uint1 adu.setDataRegister(0, startAddress); adu.setDataRegister(2, quantity); adu.setDataLen(4); - _rtuComm.writeAdu(adu); + if (_rtuComm._waiting_for_read == false) _rtuComm.writeAdu(adu); ModbusRTUCommError commError = _rtuComm.readAdu(adu); if (commError) return _translateCommError(commError); if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; @@ -443,7 +451,7 @@ ModbusRTUMasterError Modbus::_writeSingleValue(uint8_t id, uint8_t functionCode, adu.setDataRegister(0, address); adu.setDataRegister(2, value); adu.setDataLen(4); - _rtuComm.writeAdu(adu); + if (_rtuComm._waiting_for_read == false) _rtuComm.writeAdu(adu); if (id == 0) return MODBUS_RTU_MASTER_SUCCESS; ModbusRTUCommError commError = _rtuComm.readAdu(adu); if (commError) return _translateCommError(commError); @@ -471,6 +479,8 @@ ModbusRTUMasterError Modbus::_translateCommError(ModbusRTUCommError commError) { return MODBUS_RTU_MASTER_FRAME_ERROR; case MODBUS_RTU_COMM_CRC_ERROR: return MODBUS_RTU_MASTER_CRC_ERROR; + case MODBUS_RTU_COMM_WAITING: + return MODBUS_RTU_MASTER_WAITING; default: return MODBUS_RTU_MASTER_UNKNOWN_COMM_ERROR; } @@ -521,31 +531,36 @@ void Modbus::_loop(unsigned long currentMicros) { #endif uint8_t error; // send reads and writes, DIAG on errors other than 0 (Success), or 3 (Invalid Quantity) - error = writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); - if (error != 0 && error != 3) DIAG(F("ModbusHR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); -#ifdef DIAG_IO - if (error == 0) DIAG(F("ModbusHR: T%d Success!"), _currentNode->getNodeID()); -#endif - if (error != 0 && error != 3) flagOK = false; - error = writeMultipleCoils(_currentNode->getNodeID(), 0, (int*) _currentNode->coils, _currentNode->getNumCoils()); - if (error != 0 && error != 3) DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); -#ifdef DIAG_IO - if (error == 0) DIAG(F("ModbusMC: T%d Success!"), _currentNode->getNodeID()); -#endif - if (error != 0 && error != 3) flagOK = false; - error = readDiscreteInputs(_currentNode->getNodeID(), 0, (int*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); - if (error != 0 && error != 3) DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); -#ifdef DIAG_IO - if (error == 0) DIAG(F("ModbusDI: T%d Success!"), _currentNode->getNodeID()); -#endif - if (error != 0 && error != 3) flagOK = false; - error = readInputRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); - if (error != 0 && error != 3) DIAG(F("ModbusIR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); -#ifdef DIAG_IO - if (error == 0) DIAG(F("ModbusIR: T%d Success!"), _currentNode->getNodeID()); -#endif - if (error != 0 && error != 3) flagOK = false; - + switch (_operationCount) { + case 0: + error = writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); + if (error != 0 && error != 3) DIAG(F("ModbusHR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); + if (error != 0 && error != 3) flagOK = false; + break; + case 1: + error = writeMultipleCoils(_currentNode->getNodeID(), 0, (int*) _currentNode->coils, _currentNode->getNumCoils()); + if (error != 0 && error != 3) DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); + if (error != 0 && error != 3) flagOK = false; + break; + case 2: + error = readDiscreteInputs(_currentNode->getNodeID(), 0, (int*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); + if (error != 0 && error != 3) DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); + if (error != 0 && error != 3) flagOK = false; + break; + case 3: + error = readInputRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); + if (error != 0 && error != 3) DIAG(F("ModbusIR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); + if (error != 0 && error != 3) flagOK = false; + break; + } + if (error != MODBUS_RTU_MASTER_WAITING) { + if (_operationCount < 3) { + _operationCount++; + } else { + _operationCount = 0; + _currentNode = _currentNode->getNext(); + } + } #if defined(MODBUS_STM_OK) if (flagOK == true) { ArduinoPins::fastWriteDigital(MODBUS_STM_OK,HIGH); @@ -563,7 +578,7 @@ void Modbus::_loop(unsigned long currentMicros) { #if defined(MODBUS_STM_COMM) ArduinoPins::fastWriteDigital(MODBUS_STM_COMM,LOW); #endif - _currentNode = _currentNode->getNext(); + } // Link to chain of Modbus instances diff --git a/IO_Modbus.h b/IO_Modbus.h index 55398086..08db7a51 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -102,7 +102,8 @@ enum ModbusRTUCommError : uint8_t { MODBUS_RTU_COMM_SUCCESS = 0, MODBUS_RTU_COMM_TIMEOUT = 1, MODBUS_RTU_COMM_FRAME_ERROR = 2, - MODBUS_RTU_COMM_CRC_ERROR = 3 + MODBUS_RTU_COMM_CRC_ERROR = 3, + MODBUS_RTU_COMM_WAITING = 4 }; class ModbusRTUComm { @@ -111,6 +112,7 @@ class ModbusRTUComm { void begin(unsigned long baud, uint32_t config = SERIAL_8N1); void setTimeout(unsigned long timeout); ModbusRTUCommError readAdu(ModbusADU& adu); + bool _waiting_for_read = false; void writeAdu(ModbusADU& adu); void clearRxBuffer(); Stream& _serial; @@ -140,7 +142,8 @@ enum ModbusRTUMasterError : uint8_t { MODBUS_RTU_MASTER_UNEXPECTED_BYTE_COUNT = 12, MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS = 13, MODBUS_RTU_MASTER_UNEXPECTED_VALUE = 14, - MODBUS_RTU_MASTER_UNEXPECTED_QUANTITY = 15 + MODBUS_RTU_MASTER_UNEXPECTED_QUANTITY = 15, + MODBUS_RTU_MASTER_WAITING = 16 }; @@ -307,7 +310,7 @@ class Modbus : public IODevice { unsigned long _currentMicros; // last value of micros() from _loop function. 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; static Modbus *_busList; // linked list of defined bus instances ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, int buf[], uint16_t quantity); ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity); From 9837cff4a5bc7a10644a851b9c5d6d016f7e8771 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sat, 30 Nov 2024 13:33:28 -0500 Subject: [PATCH 28/73] IO_Modbus: works great now! --- IO_Modbus.cpp | 27 +++++++++++++++++++-------- IO_Modbus.h | 4 ++++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 887bf44a..dff3bf9e 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -226,6 +226,7 @@ ModbusRTUCommError ModbusRTUComm::readAdu(ModbusADU& adu) { //if (millis() - startMillis >= _readTimeout) return MODBUS_RTU_COMM_TIMEOUT; _waiting_for_read = true; if (millis() - startMillis >= _readTimeout) { + //_serial.flush(); return MODBUS_RTU_COMM_TIMEOUT; } else { return MODBUS_RTU_COMM_WAITING; @@ -534,25 +535,35 @@ void Modbus::_loop(unsigned long currentMicros) { switch (_operationCount) { case 0: error = writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); - if (error != 0 && error != 3) DIAG(F("ModbusHR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); - if (error != 0 && error != 3) flagOK = false; + if (error != 0 && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusHR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); + if (error != 0 && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; break; case 1: error = writeMultipleCoils(_currentNode->getNodeID(), 0, (int*) _currentNode->coils, _currentNode->getNumCoils()); - if (error != 0 && error != 3) DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); - if (error != 0 && error != 3) flagOK = false; + if (error != 0 && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); + if (error != 0 && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; break; case 2: error = readDiscreteInputs(_currentNode->getNodeID(), 0, (int*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); - if (error != 0 && error != 3) DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); - if (error != 0 && error != 3) flagOK = false; + if (error != 0 && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); + if (error != 0 && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; break; case 3: error = readInputRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); - if (error != 0 && error != 3) DIAG(F("ModbusIR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); - if (error != 0 && error != 3) flagOK = false; + if (error != 0 && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusIR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); + if (error != 0 && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; break; } + if (error == MODBUS_RTU_MASTER_WAITING) { + if (_waitCounter > 10) { + _resetWaiting(); + _waitCounter = 0; + } else { + _waitCounter++; + } + } else { + _waitCounter = 0; + } if (error != MODBUS_RTU_MASTER_WAITING) { if (_operationCount < 3) { _operationCount++; diff --git a/IO_Modbus.h b/IO_Modbus.h index 08db7a51..a1a00c1b 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -315,6 +315,10 @@ class Modbus : public IODevice { ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, int buf[], uint16_t quantity); ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity); ModbusRTUMasterError _writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value); + int _waitCounter = 0; + void _resetWaiting() { + _rtuComm._waiting_for_read = false; + } public: static void create(uint8_t busNo, HardwareSerial& serial, unsigned long baud, uint16_t cycleTimeMS=500, int8_t txPin=-1) { new Modbus(busNo, serial, baud, cycleTimeMS, txPin); From a31b671ea01e4bcc9aaa34b30af843059fd75106 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sat, 30 Nov 2024 14:06:23 -0500 Subject: [PATCH 29/73] IO_Modbus: move next after failure --- IO_Modbus.cpp | 11 ++++++++++- IO_Modbus.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index dff3bf9e..d1fd1858 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -555,15 +555,24 @@ void Modbus::_loop(unsigned long currentMicros) { break; } if (error == MODBUS_RTU_MASTER_WAITING) { - if (_waitCounter > 10) { + if (_waitCounter > 10) { // retry after 10 cycles of waiting. _resetWaiting(); _waitCounter = 0; + _waitCounterB++; } else { _waitCounter++; } + if (_waitCounterB > 10) { // move on to next node if fails 10 times. + _waitCounter = 0; + _waitCounterB = 0; + _operationCount = 0; + _currentNode = _currentNode->getNext(); + } } else { _waitCounter = 0; + _waitCounterB = 0; } + if (error != MODBUS_RTU_MASTER_WAITING) { if (_operationCount < 3) { _operationCount++; diff --git a/IO_Modbus.h b/IO_Modbus.h index a1a00c1b..85901653 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -316,6 +316,8 @@ class Modbus : public IODevice { ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity); ModbusRTUMasterError _writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value); int _waitCounter = 0; + int _waitCounterB = 0; + void _resetWaiting() { _rtuComm._waiting_for_read = false; } From f1217bf92f65aa27a43612b79c57f8fe5bdee67d Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sat, 30 Nov 2024 14:20:46 -0500 Subject: [PATCH 30/73] IO_Modbus: slight change --- IO_Modbus.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IO_Modbus.h b/IO_Modbus.h index 85901653..5b08f5cf 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -228,7 +228,7 @@ class Modbusnode : public IODevice { return (int) inputRegisters[pin]; } - void _writeAnalogue(VPIN vpin, int value, uint8_t param1=0, uint16_t param2=0) override { + void _writeAnalogue(VPIN vpin, int value) { uint16_t pin = vpin - _firstVpin - _numDiscreteInputs - _numCoils - _numInputRegisters; holdingRegisters[pin] = (uint16_t*) value; } @@ -317,7 +317,7 @@ class Modbus : public IODevice { ModbusRTUMasterError _writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value); int _waitCounter = 0; int _waitCounterB = 0; - + void _resetWaiting() { _rtuComm._waiting_for_read = false; } From 72e2ec54331ebfbe0d0a1172792f87f7fccf1ea7 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sun, 1 Dec 2024 00:40:04 -0500 Subject: [PATCH 31/73] IO_Modbus: Trying to improve error recovery --- IO_Modbus.cpp | 21 ++++++++++++--------- IO_Modbus.h | 13 ++++++++----- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index d1fd1858..c13658b3 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -222,10 +222,11 @@ void ModbusRTUComm::setTimeout(unsigned long timeout) { ModbusRTUCommError ModbusRTUComm::readAdu(ModbusADU& adu) { adu.setRtuLen(0); unsigned long startMillis = millis(); + if (_startTimeout == 0UL) _startTimeout = startMillis; if (!_serial.available()) { //if (millis() - startMillis >= _readTimeout) return MODBUS_RTU_COMM_TIMEOUT; _waiting_for_read = true; - if (millis() - startMillis >= _readTimeout) { + if (millis() - _startTimeout >= _readTimeout) { //_serial.flush(); return MODBUS_RTU_COMM_TIMEOUT; } else { @@ -234,6 +235,7 @@ ModbusRTUCommError ModbusRTUComm::readAdu(ModbusADU& adu) { } _waiting_for_read = false; + _startTimeout = 0UL; uint16_t len = 0; unsigned long startMicros = micros(); do { @@ -493,14 +495,15 @@ ModbusRTUMasterError Modbus::_translateCommError(ModbusRTUCommError commError) { // Constructor for Modbus -Modbus::Modbus(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin) : _rtuComm(serial, txPin) { +Modbus::Modbus(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA, int waitB) : _rtuComm(serial, txPin) { _baud = baud; _serialD = &serial; _txPin = txPin; _rtuComm.setTimeout(500); _busNo = busNo; _cycleTime = cycleTimeMS * 1000UL; // convert from milliseconds to microseconds. - + _waitA = waitA; + _waitB = waitB; // Add device to HAL device chain IODevice::addDevice(this); @@ -555,14 +558,14 @@ void Modbus::_loop(unsigned long currentMicros) { break; } if (error == MODBUS_RTU_MASTER_WAITING) { - if (_waitCounter > 10) { // retry after 10 cycles of waiting. - _resetWaiting(); + if (_waitCounter > _waitA) { // retry after 10 cycles of waiting, or user setting waitA. + _resetWaiting(); // reset Waiting flag so it retries. _waitCounter = 0; _waitCounterB++; } else { _waitCounter++; } - if (_waitCounterB > 10) { // move on to next node if fails 10 times. + if (_waitCounterB > _waitB) { // move on to next node if fails 10 times, or user setting waitB. _waitCounter = 0; _waitCounterB = 0; _operationCount = 0; @@ -573,9 +576,9 @@ void Modbus::_loop(unsigned long currentMicros) { _waitCounterB = 0; } - if (error != MODBUS_RTU_MASTER_WAITING) { - if (_operationCount < 3) { - _operationCount++; + if (error == MODBUS_RTU_MASTER_SUCCESS) { // should have the effect of retrying same opperation until success + if (_operationCount < 3) { // unless it fails waitB and moves on to next node. may even + _operationCount++; // improve error recovery... } else { _operationCount = 0; _currentNode = _currentNode->getNext(); diff --git a/IO_Modbus.h b/IO_Modbus.h index 5b08f5cf..1a42c161 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -122,8 +122,9 @@ class ModbusRTUComm { unsigned long _charTimeout; unsigned long _frameTimeout; - unsigned long _postDelay = 0; - unsigned long _readTimeout = 0; + unsigned long _postDelay = 0UL; + unsigned long _readTimeout = 0UL; + unsigned long _startTimeout = 0UL; }; enum ModbusRTUMasterError : uint8_t { @@ -317,13 +318,15 @@ class Modbus : public IODevice { ModbusRTUMasterError _writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value); int _waitCounter = 0; int _waitCounterB = 0; + int _waitA; + int _waitB; void _resetWaiting() { _rtuComm._waiting_for_read = false; } public: - static void create(uint8_t busNo, HardwareSerial& serial, unsigned long baud, uint16_t cycleTimeMS=500, int8_t txPin=-1) { - new Modbus(busNo, serial, baud, cycleTimeMS, txPin); + 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 Modbus(busNo, serial, baud, cycleTimeMS, txPin, waitA, waitB); } HardwareSerial *_serialD; ModbusRTUComm _rtuComm; @@ -388,7 +391,7 @@ class Modbus : public IODevice { } protected: - Modbus(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin); + Modbus(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA, int waitB); public: From 7d0cfbc742621dd210919b1b2ff4bb67c9ddcca2 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sun, 1 Dec 2024 00:43:55 -0500 Subject: [PATCH 32/73] IO_Modbus: fixed odd bracket indentation --- IO_Modbus.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index c13658b3..74929f94 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -602,7 +602,7 @@ void Modbus::_loop(unsigned long currentMicros) { ArduinoPins::fastWriteDigital(MODBUS_STM_COMM,LOW); #endif - } +} // Link to chain of Modbus instances Modbus *Modbus::_busList = NULL; From f47e417c8f91503ad5f00b78ceaf4d01fc022b86 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sun, 1 Dec 2024 09:42:55 -0500 Subject: [PATCH 33/73] IO_Modbus: tidy up some constant references --- IO_Modbus.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 74929f94..4e9da46a 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -538,23 +538,23 @@ void Modbus::_loop(unsigned long currentMicros) { switch (_operationCount) { case 0: error = writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); - if (error != 0 && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusHR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); - if (error != 0 && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; + if (error != MODBUS_RTU_MASTER_SUCCESS && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusHR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); + if (error != MODBUS_RTU_MASTER_SUCCESS && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; break; case 1: error = writeMultipleCoils(_currentNode->getNodeID(), 0, (int*) _currentNode->coils, _currentNode->getNumCoils()); - if (error != 0 && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); - if (error != 0 && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; + if (error != MODBUS_RTU_MASTER_SUCCESS && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); + if (error != MODBUS_RTU_MASTER_SUCCESS && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; break; case 2: error = readDiscreteInputs(_currentNode->getNodeID(), 0, (int*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); - if (error != 0 && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); - if (error != 0 && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; + if (error != MODBUS_RTU_MASTER_SUCCESS && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); + if (error != MODBUS_RTU_MASTER_SUCCESS && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; break; case 3: error = readInputRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); - if (error != 0 && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusIR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); - if (error != 0 && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; + if (error != MODBUS_RTU_MASTER_SUCCESS && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusIR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); + if (error != MODBUS_RTU_MASTER_SUCCESS && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; break; } if (error == MODBUS_RTU_MASTER_WAITING) { From 813742332599c6139d9851fc8b519b5c630bd9de Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sun, 1 Dec 2024 12:26:12 -0500 Subject: [PATCH 34/73] IO_Modbus: board support limits --- IO_Modbus.cpp | 5 ++++- IO_Modbus.h | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 4e9da46a..0ecb098e 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with CommandStation. If not, see . */ - +#if defined(MBEXPERIMENTAL) || defined(ARDUINO_ARCH_STM32) #include "IO_Modbus.h" #include "defines.h" void ModbusADU::setTransactionId(uint16_t transactionId) { @@ -504,6 +504,8 @@ Modbus::Modbus(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint16 _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); @@ -643,3 +645,4 @@ Modbusnode::Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, } } +#endif \ No newline at end of file diff --git a/IO_Modbus.h b/IO_Modbus.h index 1a42c161..203929da 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -50,7 +50,7 @@ #ifndef IO_MODBUS_H #define IO_MODBUS_H - +#if defined(MBEXPERIMENTAL) || defined(ARDUINO_ARCH_STM32) #include "IODevice.h" class ModbusADU { public: @@ -406,5 +406,7 @@ class Modbus : public IODevice { return NULL; } }; - +#else +#error "You have included IO_Modbus on an unsupported board!" +#endif #endif // IO_MODBUS_H From 4ad07e01ee4045d47253a7b280444e411bb26def Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Mon, 2 Dec 2024 04:06:06 -0500 Subject: [PATCH 35/73] IO_Modbus: corrected attribution --- IO_Modbus.cpp | 1 + IO_Modbus.h | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 0ecb098e..1da52230 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -1,5 +1,6 @@ /* * © 2024, Travis Farmer. All rights reserved. + * © 2024, Chris Bulliner. All rights reserved. https://github.com/CMB27 * * This file is part of DCC++EX API * diff --git a/IO_Modbus.h b/IO_Modbus.h index 203929da..070365e1 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -1,6 +1,7 @@ /* * © 2024, Travis Farmer. All rights reserved. - * + * © 2024, Chris Bulliner. All rights reserved. https://github.com/CMB27 + * * This file is part of DCC++EX API * * This is free software: you can redistribute it and/or modify @@ -406,7 +407,6 @@ class Modbus : public IODevice { return NULL; } }; -#else -#error "You have included IO_Modbus on an unsupported board!" + #endif #endif // IO_MODBUS_H From 3cb6a35ddef2754418560062131976bb85d2a914 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Mon, 2 Dec 2024 14:15:06 -0500 Subject: [PATCH 36/73] IO_Modbus: removed board limits. too annoying. --- IO_Modbus.cpp | 3 +-- IO_Modbus.h | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 1da52230..f7b0b224 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with CommandStation. If not, see . */ -#if defined(MBEXPERIMENTAL) || defined(ARDUINO_ARCH_STM32) + #include "IO_Modbus.h" #include "defines.h" void ModbusADU::setTransactionId(uint16_t transactionId) { @@ -646,4 +646,3 @@ Modbusnode::Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, } } -#endif \ No newline at end of file diff --git a/IO_Modbus.h b/IO_Modbus.h index 070365e1..d0f4779b 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -51,7 +51,7 @@ #ifndef IO_MODBUS_H #define IO_MODBUS_H -#if defined(MBEXPERIMENTAL) || defined(ARDUINO_ARCH_STM32) + #include "IODevice.h" class ModbusADU { public: @@ -408,5 +408,5 @@ class Modbus : public IODevice { } }; -#endif + #endif // IO_MODBUS_H From 77f1c3c99ff854569cdaf75feb8da8537fe2938a Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Wed, 11 Dec 2024 14:21:04 -0500 Subject: [PATCH 37/73] first prototype EX-MB-ioexpander, CS side --- IO_Modbus.cpp | 25 ++---- IO_Modbus.h | 245 ++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 233 insertions(+), 37 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index f7b0b224..b637e550 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -550,13 +550,13 @@ void Modbus::_loop(unsigned long currentMicros) { if (error != MODBUS_RTU_MASTER_SUCCESS && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; break; case 2: - error = readDiscreteInputs(_currentNode->getNodeID(), 0, (int*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); - if (error != MODBUS_RTU_MASTER_SUCCESS && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); + error = readInputRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); + if (error != MODBUS_RTU_MASTER_SUCCESS && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusIR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); if (error != MODBUS_RTU_MASTER_SUCCESS && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; break; case 3: - error = readInputRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); - if (error != MODBUS_RTU_MASTER_SUCCESS && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusIR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); + error = readDiscreteInputs(_currentNode->getNodeID(), 0, (int*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); + if (error != MODBUS_RTU_MASTER_SUCCESS && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); if (error != MODBUS_RTU_MASTER_SUCCESS && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; break; } @@ -572,6 +572,7 @@ void Modbus::_loop(unsigned long currentMicros) { _waitCounter = 0; _waitCounterB = 0; _operationCount = 0; + _currentNode = _currentNode->getNext(); } } else { @@ -584,6 +585,7 @@ void Modbus::_loop(unsigned long currentMicros) { _operationCount++; // improve error recovery... } else { _operationCount = 0; + _currentNode->procData(); _currentNode = _currentNode->getNext(); } } @@ -616,25 +618,12 @@ Modbus *Modbus::_busList = NULL; ************************************************************/ // Constructor for Modbusnode object -Modbusnode::Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils, uint8_t numDiscreteInputs, uint8_t numHoldingRegisters, uint8_t numInputRegisters) { +Modbusnode::Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID) { _firstVpin = firstVpin; _nPins = nPins; _busNo = busNo; _nodeID = nodeID; - _numCoils = numCoils; - _numDiscreteInputs = numDiscreteInputs; - _numHoldingRegisters = numHoldingRegisters; - _numInputRegisters = numInputRegisters; - - if ((unsigned int)_nPins < numDiscreteInputs + numCoils) - DIAG(F("Modbusnode: bus:%d nodeID:%d WARNING number of Vpins does not cover all inputs and outputs"), _busNo, _nodeID); - - if (!discreteInputs || !coils) { - DIAG(F("Modbusnode: ERROR insufficient memory")); - return; - } - // Add this device to HAL device list IODevice::addDevice(this); _display(); diff --git a/IO_Modbus.h b/IO_Modbus.h index d0f4779b..6eb2fbb6 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -163,20 +163,76 @@ class Modbusnode : public IODevice { char _type; Modbusnode *_next = NULL; bool _initialised = false; - uint8_t _numCoils; - uint8_t _numDiscreteInputs; - uint8_t _numHoldingRegisters; - uint8_t _numInputRegisters; + static const uint8_t _numCoils=100; + static const uint8_t _numDiscreteInputs=100; + static const uint8_t _numHoldingRegisters=100; + static const uint8_t _numInputRegisters=100; + uint8_t _numBO=0; + uint8_t _numBI=0; + uint8_t _numAO=0; + uint8_t _numAI=0; + int dataBO[16]; + int dataBI[16]; + int dataAO[84]; + int dataAI[84]; + int capePinsBI[16]; + int capePinsBO[16]; + int capePinsPU[16]; + int capePinsAO[16]; + int capePinsAI[16]; + int configBPinsO[16]; + int configBPinsI[16]; + int configBPinsPU[16]; + int configAPinsO[16]; + int configAPinsI[16]; + + void resetInit() { + for (int i = 0; i < 16; i++) { + capePinsBI[i] = 0; + capePinsBO[i] = 0; + capePinsPU[i] = 0; + capePinsAO[i] = 0; + capePinsAI[i] = 0; + configBPinsO[i] = 0; + configBPinsI[i] = 0; + configBPinsPU[i] = 0; + configAPinsO[i] = 0; + configAPinsI[i] = 0; + } + } + + + void spitError(int pin) { + bool isBI = false; + bool isBO = false; + bool isPU = false; + bool isAI = false; + bool isAO = false; + int configPinNum = pin / 16; + int configPinBit = pin % 16; + if (bitRead(configBPinsI[configPinNum],configPinBit) == true) isBI = true; + if (bitRead(configBPinsO[configPinNum],configPinBit) == true) isBO = true; + if (bitRead(configBPinsPU[configPinNum],configPinBit) == true) isPU = true; + if (bitRead(configAPinsI[configPinNum],configPinBit) == true) isAI = true; + if (bitRead(configAPinsO[configPinNum],configPinBit) == true) isAO = true; + if (isBI && isPU) DIAG(F("IO_Modbus config eror: Bool Input with pull-up, pin: %d"),pin); + if (isBI && !isPU) DIAG(F("IO_Modbus config eror: Bool Input without pull-up, pin: %d"),pin); + if (isBO) DIAG(F("IO_Modbus config eror: Bool Output, pin: %d"),pin); + if (isAI) DIAG(F("IO_Modbus config eror: Analog Input, pin: %d"),pin); + if (isAO) DIAG(F("IO_Modbus config eror: Analog Output, pin: %d"),pin); + + } + public: - static void create(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils=0, uint8_t numDiscreteInputs=0, uint8_t numHoldingRegisters=0, uint8_t numInputRegisters=0) { - if (checkNoOverlap(firstVpin, nPins)) new Modbusnode(firstVpin, nPins, busNo, nodeID, numCoils, numDiscreteInputs, numHoldingRegisters, numInputRegisters); + static void create(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID) { + if (checkNoOverlap(firstVpin, nPins)) new Modbusnode(firstVpin, nPins, busNo, nodeID); } - Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID, uint8_t numCoils, uint8_t numDiscreteInputs, uint8_t numHoldingRegisters, uint8_t numInputRegisters); - int *coils[100]; - int *discreteInputs[100]; - uint16_t *holdingRegisters[100]; - uint16_t *inputRegisters[100]; + Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID); + int *coils[_numCoils]; + int *discreteInputs[_numDiscreteInputs]; + uint16_t *holdingRegisters[_numHoldingRegisters]; + uint16_t *inputRegisters[_numInputRegisters]; uint8_t getNodeID() { return _nodeID; @@ -193,6 +249,8 @@ class Modbusnode : public IODevice { uint8_t getNumInputRegisters() { return _numInputRegisters; } + + Modbusnode *getNext() { return _next; } @@ -206,33 +264,181 @@ class Modbusnode : public IODevice { _initialised = true; } + bool addPinBI(VPIN vpin, bool inputPullup) { + int configPinNum = vpin / 16; + int configPinBit = vpin % 16; + bitSet(configBPinsI[configPinNum],configPinBit); // input + bitWrite(configBPinsPU[configPinNum],configPinBit,inputPullup); + if (_numBI + _numBO + _numAI + _numAO > _nPins) { + DIAG(F("IO_Modbus config error: Too many I/O pins vs VPINs: %d"),_numBI + _numBO + _numAI + _numAO); + return true; + } + _numBI++; + return false; + } + + bool addPinBO(VPIN vpin) { + int configPinNum = vpin / 16; + int configPinBit = vpin % 16; + bitSet(configBPinsO[configPinNum],configPinBit); // input + if (_numBI + _numBO + _numAI + _numAO > _nPins) { + DIAG(F("IO_Modbus config error: Too many I/O pins vs VPINs: %d"),_numBI + _numBO + _numAI + _numAO); + return true; + } + _numBO++; + return false; + } + + bool addPinAI(VPIN vpin) { + int configPinNum = vpin / 6; + int configPinBit = vpin % 16; + bitSet(configAPinsI[configPinNum],configPinBit); // input + if (_numBI + _numBO + _numAI + _numAO > _nPins) { + DIAG(F("IO_Modbus config error: Too many I/O pins vs VPINs: %d"),_numBI + _numBO + _numAI + _numAO); + return true; + } + _numAI++; + return false; + } + + bool addPinAO(VPIN vpin) { + int configPinNum = vpin / 6; + int configPinBit = vpin % 16; + bitSet(configAPinsO[configPinNum],configPinBit); // input + if (_numBI + _numBO + _numAI + _numAO > _nPins) { + DIAG(F("IO_Modbus config error: Too many I/O pins vs VPINs: %d"),_numBI + _numBO + _numAI + _numAO); + return true; + } + _numBI++; + return false; + } + + bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override { + byte arduinoPin = params[1]; + if (paramCount != 1) return false; + int pin = vpin - _firstVpin; + if (configType == CONFIGURE_INPUT) { + bool inputPullup = false; + if (params[2] == 1) inputPullup = true; + bool status = addPinBI(vpin,inputPullup); + if (status == false) { + return true; + } else + DIAG(F("IO_Modbus Vpin %u, Arduino Pin %d, cannot be used as a digital input pin"), (int)vpin, arduinoPin); + } else if (configType == CONFIGURE_OUTPUT) { + bool status = addPinBO(vpin); + if (status == false) { + return true; + } else + DIAG(F("IO_Modbus Vpin %u, Arduino Pin %d, cannot be used as a digital Output pin"), (int)vpin, arduinoPin); + } else if (configType == CONFIGURE_SERVO) { + //blah + } else if (configType == CONFIGURE_ANALOGOUTPUT) { + bool status = addPinAO(vpin); + if (status == false) { + return true; + } else + DIAG(F("IO_Modbus Vpin %u, Arduino Pin %d, cannot be used as a analog Output pin"), (int)vpin, arduinoPin); + } else if (configType == CONFIGURE_ANALOGINPUT) { + bool status = addPinAI(vpin); + if (status == false) { + return true; + } else + DIAG(F("IO_Modbus Vpin %u, Arduino Pin %d, cannot be used as a analog Input pin"), (int)vpin, arduinoPin); + } + return false; + } + void _begin() override { + resetInit(); + coils[0] = (int*) 1; // set config mode + coils[1] = (int*) 1; // set pull capabilities + _initialised = false; } + + + void procData() { + if (isInitialised()) { // read/write data + for (int i = 0; i < 16; i++) { + holdingRegisters[i] = (uint16_t*) dataBO[i]; + dataBI[i] = (int) inputRegisters[i]; + } + for (int i = 16; i < 84; i++) { + holdingRegisters[i] = (uint16_t*) dataAO[i]; + dataAI[i] = (int) inputRegisters[i]; + } + } else { // read/write config + if (discreteInputs[0] == (int*) 1 && discreteInputs[1] == (int*) 1){ // get capabilities + for (int i = 0; i < 16; i++) { // read capabilities params + capePinsBI[i] = (int) inputRegisters[i]; + capePinsBO[i] = (int) inputRegisters[i+16]; + capePinsPU[i] = (int) inputRegisters[i+32]; + capePinsAI[i] = (int) inputRegisters[i+48]; + capePinsAO[i] = (int) inputRegisters[i+64]; + } + coils[0] = (int*) 1; // config mode + coils[1] = (int*) 0; // exit cape mode + for (int i = 0; i < 16; i++) { // load config params + holdingRegisters[i] = (uint16_t*) configBPinsI[i]; + holdingRegisters[i+16] = (uint16_t*) configBPinsO[i]; + holdingRegisters[i+32] = (uint16_t*) configBPinsPU[i]; + holdingRegisters[i+48] = (uint16_t*) configAPinsI[i]; + holdingRegisters[i+64] = (uint16_t*) configAPinsO[i]; + } + } else if (discreteInputs[0] == (int*) 1 && discreteInputs[1] == (int*) 0) { + for (int i = 0; i < 16; i++) { + if (configBPinsI[i] != (int) inputRegisters[i]) spitError(i); + if (configBPinsO[i] != (int) inputRegisters[i+16]) spitError(i+16); + if (configBPinsPU[i] != (int) inputRegisters[i+32]) spitError(i+32); + if (configAPinsI[i] != (int) inputRegisters[i+48]) spitError(i+48); + if (configAPinsO[i] != (int) inputRegisters[i+64]) spitError(i+64); + } + // todo error checking and set failure mode + // for now, assume everything is fine, sort this out later if needed + coils[0] = (int*) 0; // exit config mode + coils[1] = (int*) 0; // not in cape mode + setInitialised(); + } + } + } + int _read(VPIN vpin) override { // Return current state from this device uint16_t pin = vpin - _firstVpin; - return (int) discreteInputs[pin]; + int PinNum = pin / 16; + int PinBit = pin % 16; + if (bitRead(configAPinsI[PinNum],PinBit) == true) return bitRead(dataBI[PinNum],PinBit)? 1:0; + else return 0; } void _write(VPIN vpin, int value) override { // Update current state for this device, in preparation the bus transmission - uint16_t pin = vpin - _firstVpin - _numDiscreteInputs; - if (value == 1) coils[pin] = (int*) 0x1; - if (value == 0) coils[pin] = (int*) 0x0; + uint16_t pin = vpin - _firstVpin; + int PinNum = pin / 16; + int PinBit = pin % 16; + if (bitRead(configAPinsO[PinNum], PinBit) == true) { + if (value == 1) bitSet(dataBO[PinNum], PinBit); + else bitClear(dataBO[PinNum], PinBit); + } } int _readAnalogue(VPIN vpin) { // Return acquired data value, e.g. - int pin = vpin - _firstVpin - _numDiscreteInputs - _numCoils; - return (int) inputRegisters[pin]; + uint16_t pin = vpin - _firstVpin; + int PinNum = pin / 16; + int PinBit = pin % 16; + if (bitRead(configAPinsI[PinNum],PinBit) == true) return dataAI[pin]; + else return 0; } void _writeAnalogue(VPIN vpin, int value) { - uint16_t pin = vpin - _firstVpin - _numDiscreteInputs - _numCoils - _numInputRegisters; - holdingRegisters[pin] = (uint16_t*) value; + uint16_t pin = vpin - _firstVpin; + int PinNum = pin / 16; + int PinBit = pin % 16; + if (bitRead(configAPinsI[PinNum],PinBit) == true) dataAO[pin] = value; } uint8_t getBusNumber() { @@ -380,6 +586,7 @@ class Modbus : public IODevice { return NULL; } + // Add new Modbusnode to the list of nodes for this bus. void addNode(Modbusnode *newNode) { if (!_nodeListStart) From 9c8bdc77284afff4ee1ca85f3cf78c632861d66e Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Thu, 12 Dec 2024 10:31:32 -0500 Subject: [PATCH 38/73] saving current --- IO_Modbus.cpp | 425 +++++--------------------------------------- IO_Modbus.h | 484 +++++++++++++++++++++++++++++++------------------- 2 files changed, 341 insertions(+), 568 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index b637e550..490992a9 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -20,98 +20,100 @@ #include "IO_Modbus.h" #include "defines.h" -void ModbusADU::setTransactionId(uint16_t transactionId) { + + +void Modbus::setTransactionId(uint16_t transactionId) { _setRegister(tcp, 0, transactionId); } -void ModbusADU::setProtocolId(uint16_t protocolId) { +void Modbus::setProtocolId(uint16_t protocolId) { _setRegister(tcp, 2, protocolId); } -void ModbusADU::setLength(uint16_t length) { +void Modbus::setLength(uint16_t length) { if (length < 3 || length > 254) _setRegister(tcp, 4, 0); else _setRegister(tcp, 4, length); } -void ModbusADU::setUnitId(uint8_t unitId) { +void Modbus::setUnitId(uint8_t unitId) { tcp[6] = unitId; } -void ModbusADU::setFunctionCode(uint8_t functionCode) { +void Modbus::setFunctionCode(uint8_t functionCode) { pdu[0] = functionCode; } -void ModbusADU::setDataRegister(uint8_t index, uint16_t value) { +void Modbus::setDataRegister(uint8_t index, uint16_t value) { _setRegister(data, index, value); } -void ModbusADU::setRtuLen(uint16_t rtuLen) { +void Modbus::setRtuLen(uint16_t rtuLen) { setLength(rtuLen - 2); } -void ModbusADU::setTcpLen(uint16_t tcpLen) { +void Modbus::setTcpLen(uint16_t tcpLen) { setLength(tcpLen - 6); } -void ModbusADU::setPduLen(uint16_t pduLen) { +void Modbus::setPduLen(uint16_t pduLen) { setLength(pduLen + 1); } -void ModbusADU::setDataLen(uint16_t dataLen) { +void Modbus::setDataLen(uint16_t dataLen) { setLength(dataLen + 2); } -uint16_t ModbusADU::getTransactionId() { +uint16_t Modbus::getTransactionId() { return _getRegister(tcp, 0); } -uint16_t ModbusADU::getProtocolId() { +uint16_t Modbus::getProtocolId() { return _getRegister(tcp, 2); } -uint16_t ModbusADU::getLength() { +uint16_t Modbus::getLength() { uint16_t length = _getRegister(tcp, 4); if (length < 3 || length > 254) return 0; else return length; } -uint8_t ModbusADU::getUnitId() { +uint8_t Modbus::getUnitId() { return tcp[6]; } -uint8_t ModbusADU::getFunctionCode() { +uint8_t Modbus::getFunctionCode() { return pdu[0]; } -uint16_t ModbusADU::getDataRegister(uint8_t index) { +uint16_t Modbus::getDataRegister(uint8_t index) { return _getRegister(data, index); } -uint16_t ModbusADU::getRtuLen() { +uint16_t Modbus::getRtuLen() { uint16_t len = getLength(); if (len == 0) return 0; else return len + 2; } -uint16_t ModbusADU::getTcpLen() { +uint16_t Modbus::getTcpLen() { uint16_t len = getLength(); if (len == 0) return 0; else return len + 6; } -uint16_t ModbusADU::getPduLen() { +uint16_t Modbus::getPduLen() { uint16_t len = getLength(); if (len == 0) return 0; else return len - 1; } -uint16_t ModbusADU::getDataLen() { +uint16_t Modbus::getDataLen() { uint16_t len = getLength(); if (len == 0) return 0; else return len - 2; @@ -119,44 +121,32 @@ uint16_t ModbusADU::getDataLen() { -void ModbusADU::updateCrc() { - uint16_t len = getLength(); - uint16_t crc = _calculateCrc(len); - rtu[len] = lowByte(crc); - rtu[len + 1] = highByte(crc); +void Modbus::updateCrc(uint8_t *buf, uint16_t len) { + uint16_t crc = _calculateCrc(buf, len); + buf[len] = lowByte(crc); + buf[len + 1] = highByte(crc); } -bool ModbusADU::crcGood() { - uint16_t len = getLength(); - uint16_t aduCrc = rtu[len] | (rtu[len + 1] << 8); - uint16_t calculatedCrc = _calculateCrc(len); +bool Modbus::crcGood(uint8_t *buf, uint16_t len) { + uint16_t aduCrc = buf[len] | (buf[len + 1] << 8); + uint16_t calculatedCrc = _calculateCrc(buf, len); if (aduCrc == calculatedCrc) return true; else return false; } - - -void ModbusADU::prepareExceptionResponse(uint8_t exceptionCode) { - pdu[0] |= 0x80; - pdu[1] = exceptionCode; - setPduLen(2); -} - - - -void ModbusADU::_setRegister(uint8_t *buf, uint16_t index, uint16_t value) { +void Modbus::_setRegister(uint8_t *buf, uint16_t index, uint16_t value) { buf[index] = highByte(value); buf[index + 1] = lowByte(value); } -uint16_t ModbusADU::_getRegister(uint8_t *buf, uint16_t index) { +uint16_t Modbus::_getRegister(uint8_t *buf, uint16_t index) { return (buf[index] << 8) | buf[index + 1]; } -uint16_t ModbusADU::_calculateCrc(uint16_t len) { +uint16_t Modbus::_calculateCrc(uint8_t *buf, uint16_t len) { uint16_t value = 0xFFFF; for (uint16_t i = 0; i < len; i++) { - value ^= (uint16_t)rtu[i]; + value ^= (uint16_t)buf[i]; for (uint8_t j = 0; j < 8; j++) { bool lsb = value & 1; value >>= 1; @@ -172,335 +162,27 @@ uint16_t div8RndUp(uint16_t value) { return (value + 7) >> 3; } -ModbusRTUComm::ModbusRTUComm(Stream& serial, VPIN dePin, VPIN rePin) : _serial(serial) { - _dePin = dePin; - _rePin = rePin; -} - -void ModbusRTUComm::begin(unsigned long baud, uint32_t config) { - unsigned long bitsPerChar; - switch (config) { - case SERIAL_8E2: - case SERIAL_8O2: - bitsPerChar = 12; - break; - case SERIAL_8N2: - case SERIAL_8E1: - case SERIAL_8O1: - bitsPerChar = 11; - break; - case SERIAL_8N1: - default: - bitsPerChar = 10; - break; - } - if (baud <= 19200) { - _charTimeout = (bitsPerChar * 2500000) / baud; - _frameTimeout = (bitsPerChar * 4500000) / baud; - } - else { - _charTimeout = (bitsPerChar * 1000000) / baud + 750; - _frameTimeout = (bitsPerChar * 1000000) / baud + 1750; - } - #if defined(ARDUINO_UNOR4_MINIMA) || defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_GIGA) || (defined(ARDUINO_NANO_RP2040_CONNECT) && defined(ARDUINO_ARCH_MBED)) - _postDelay = ((bitsPerChar * 1000000) / baud) + 2; - #endif - if (_dePin != VPIN_NONE) { - pinMode(_dePin, OUTPUT); - ArduinoPins::fastWriteDigital(_dePin, LOW); - } - if (_rePin != VPIN_NONE) { - pinMode(_rePin, OUTPUT); - ArduinoPins::fastWriteDigital(_rePin, LOW); - } - clearRxBuffer(); -} - -void ModbusRTUComm::setTimeout(unsigned long timeout) { - _readTimeout = timeout; -} - -ModbusRTUCommError ModbusRTUComm::readAdu(ModbusADU& adu) { - adu.setRtuLen(0); - unsigned long startMillis = millis(); - if (_startTimeout == 0UL) _startTimeout = startMillis; - if (!_serial.available()) { - //if (millis() - startMillis >= _readTimeout) return MODBUS_RTU_COMM_TIMEOUT; - _waiting_for_read = true; - if (millis() - _startTimeout >= _readTimeout) { - //_serial.flush(); - return MODBUS_RTU_COMM_TIMEOUT; - } else { - return MODBUS_RTU_COMM_WAITING; - } - - } - _waiting_for_read = false; - _startTimeout = 0UL; - uint16_t len = 0; - unsigned long startMicros = micros(); - do { - if (_serial.available()) { - startMicros = micros(); - adu.rtu[len] = _serial.read(); - len++; - } - } while (micros() - startMicros <= _charTimeout && len < 256); - adu.setRtuLen(len); - while (micros() - startMicros < _frameTimeout); - if (_serial.available()) { - adu.setRtuLen(0); - return MODBUS_RTU_COMM_FRAME_ERROR; - } - if (!adu.crcGood()) { - adu.setRtuLen(0); - return MODBUS_RTU_COMM_CRC_ERROR; - } - return MODBUS_RTU_COMM_SUCCESS; -} - -void ModbusRTUComm::writeAdu(ModbusADU& adu) { - adu.updateCrc(); - if (_dePin != VPIN_NONE) ArduinoPins::fastWriteDigital(_dePin, HIGH); - if (_rePin != VPIN_NONE) ArduinoPins::fastWriteDigital(_rePin, HIGH); - _serial.write(adu.rtu, adu.getRtuLen()); - _serial.flush(); - ///delayMicroseconds(_postDelay); // TJF: Commented out as Mbed platforms are not supported - if (_dePin != VPIN_NONE) ArduinoPins::fastWriteDigital(_dePin, LOW); - if (_rePin != VPIN_NONE) ArduinoPins::fastWriteDigital(_rePin, LOW); -} - -void ModbusRTUComm::clearRxBuffer() { +void Modbus::clearRxBuffer() { unsigned long startMicros = micros(); do { - if (_serial.available() > 0) { + if (_serialD->available() > 0) { startMicros = micros(); - _serial.read(); + _serialD->read(); } } while (micros() - startMicros < _frameTimeout); } -void Modbus::setTimeout(unsigned long timeout) { - _rtuComm.setTimeout(timeout); -} - - - - -ModbusRTUMasterError Modbus::readCoils(uint8_t id, uint16_t startAddress, int buf[], uint16_t quantity) { - return _readValues(id, 1, startAddress, buf, quantity); -} - -ModbusRTUMasterError Modbus::readDiscreteInputs(uint8_t id, uint16_t startAddress, int buf[], uint16_t quantity) { - return _readValues(id, 2, startAddress, buf, quantity); -} - -ModbusRTUMasterError Modbus::readHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { - return _readValues(id, 3, startAddress, buf, quantity); -} - -ModbusRTUMasterError Modbus::readInputRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { - return _readValues(id, 4, startAddress, buf, quantity); -} - - - -ModbusRTUMasterError Modbus::writeSingleCoil(uint8_t id, uint16_t address, int value) { - return _writeSingleValue(id, 5, address, ((value) ? 0xFF00 : 0x0000)); -} - -ModbusRTUMasterError Modbus::writeSingleHoldingRegister(uint8_t id, uint16_t address, uint16_t value) { - return _writeSingleValue(id, 6, address, value); -} - - - -ModbusRTUMasterError Modbus::writeMultipleCoils(uint8_t id, uint16_t startAddress, int buf[], uint16_t quantity) { - const uint8_t functionCode = 15; - if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; - if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; - if (quantity == 0 || quantity > 1968) return MODBUS_RTU_MASTER_INVALID_QUANTITY; - ModbusADU adu; - uint16_t byteCount = div8RndUp(quantity); - adu.setUnitId(id); - adu.setFunctionCode(functionCode); - adu.setDataRegister(0, startAddress); - adu.setDataRegister(2, quantity); - adu.data[4] = byteCount; - for (uint16_t i = 0; i < quantity; i++) { - bitWrite(adu.data[5 + (i >> 3)], i & 7, buf[i]); - } - for (uint16_t i = quantity; i < (byteCount * 8); i++) { - bitClear(adu.data[5 + (i >> 3)], i & 7); - } - adu.setDataLen(5 + byteCount); - if (_rtuComm._waiting_for_read == false) _rtuComm.writeAdu(adu); - if (id == 0) return MODBUS_RTU_MASTER_SUCCESS; - ModbusRTUCommError commError = _rtuComm.readAdu(adu); - if (commError) return _translateCommError(commError); - if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; - if (adu.getFunctionCode() == (functionCode + 0x80)) { - _exceptionResponse = adu.data[0]; - return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; - } - if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; - if (adu.getDataLen() != 4) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; - if (adu.getDataRegister(0) != startAddress) return MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS; - if (adu.getDataRegister(2) != quantity) return MODBUS_RTU_MASTER_UNEXPECTED_QUANTITY; - return MODBUS_RTU_MASTER_SUCCESS; -} - -ModbusRTUMasterError Modbus::writeMultipleHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { - uint8_t functionCode = 16; - if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; - if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; - if (quantity == 0 || quantity > 123) return MODBUS_RTU_MASTER_INVALID_QUANTITY; - uint16_t byteCount = quantity * 2; - ModbusADU adu; - adu.setUnitId(id); - adu.setFunctionCode(functionCode); - adu.setDataRegister(0, startAddress); - adu.setDataRegister(2, quantity); - adu.data[4] = byteCount; - for (uint16_t i = 0; i < quantity; i++) { - adu.setDataRegister(5 + (i * 2), buf[i]); - } - adu.setDataLen(5 + byteCount); - if (_rtuComm._waiting_for_read == false) _rtuComm.writeAdu(adu); - if (id == 0) return MODBUS_RTU_MASTER_SUCCESS; - ModbusRTUCommError commError = _rtuComm.readAdu(adu); - if (commError) return _translateCommError(commError); - if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; - if (adu.getFunctionCode() == (functionCode + 0x80)) { - _exceptionResponse = adu.data[0]; - return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; - } - if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; - if (adu.getDataLen() != 4) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; - if (adu.getDataRegister(0) != startAddress) return MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS; - if (adu.getDataRegister(2) != quantity) return MODBUS_RTU_MASTER_UNEXPECTED_QUANTITY; - return MODBUS_RTU_MASTER_SUCCESS; -} - - - -uint8_t Modbus::getExceptionResponse() { - return _exceptionResponse; -} - - - -ModbusRTUMasterError Modbus::_readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, int buf[], uint16_t quantity) { - if (id < 1 || id > 247) return MODBUS_RTU_MASTER_INVALID_ID; - if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; - if (quantity == 0 || quantity > 2000) return MODBUS_RTU_MASTER_INVALID_QUANTITY; - ModbusADU adu; - adu.setUnitId(id); - adu.setFunctionCode(functionCode); - adu.setDataRegister(0, startAddress); - adu.setDataRegister(2, quantity); - adu.setDataLen(4); - if (_rtuComm._waiting_for_read == false) _rtuComm.writeAdu(adu); - ModbusRTUCommError commError = _rtuComm.readAdu(adu); - if (commError) return _translateCommError(commError); - if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; - if (adu.getFunctionCode() == (functionCode + 0x80)) { - _exceptionResponse = adu.data[0]; - return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; - } - if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; - uint16_t byteCount = div8RndUp(quantity); - if (adu.getDataLen() != (1 + byteCount)) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; - if (adu.data[0] != byteCount) return MODBUS_RTU_MASTER_UNEXPECTED_BYTE_COUNT; - for (uint16_t i = 0; i < quantity; i++) { - buf[i] = (int) bitRead(adu.data[1 + (i >> 3)], i & 7)? 1:0; - } - return MODBUS_RTU_MASTER_SUCCESS; -} - -ModbusRTUMasterError Modbus::_readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity) { - if (id < 1 || id > 247) return MODBUS_RTU_MASTER_INVALID_ID; - if (!buf) return MODBUS_RTU_MASTER_INVALID_BUFFER; - if (quantity == 0 || quantity > 125) return MODBUS_RTU_MASTER_INVALID_QUANTITY; - ModbusADU adu; - adu.setUnitId(id); - adu.setFunctionCode(functionCode); - adu.setDataRegister(0, startAddress); - adu.setDataRegister(2, quantity); - adu.setDataLen(4); - if (_rtuComm._waiting_for_read == false) _rtuComm.writeAdu(adu); - ModbusRTUCommError commError = _rtuComm.readAdu(adu); - if (commError) return _translateCommError(commError); - if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; - if (adu.getFunctionCode() == (functionCode + 0x80)) { - _exceptionResponse = adu.data[0]; - return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; - } - if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; - uint16_t byteCount = quantity * 2; - if (adu.getDataLen() != (1 + byteCount)) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; - if (adu.data[0] != byteCount) return MODBUS_RTU_MASTER_UNEXPECTED_BYTE_COUNT; - for (uint16_t i = 0; i < quantity; i++) { - buf[i] = adu.getDataRegister(1 + (i * 2)); - } - return MODBUS_RTU_MASTER_SUCCESS; -} - -ModbusRTUMasterError Modbus::_writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value) { - if (id > 247) return MODBUS_RTU_MASTER_INVALID_ID; - ModbusADU adu; - adu.setUnitId(id); - adu.setFunctionCode(functionCode); - adu.setDataRegister(0, address); - adu.setDataRegister(2, value); - adu.setDataLen(4); - if (_rtuComm._waiting_for_read == false) _rtuComm.writeAdu(adu); - if (id == 0) return MODBUS_RTU_MASTER_SUCCESS; - ModbusRTUCommError commError = _rtuComm.readAdu(adu); - if (commError) return _translateCommError(commError); - if (adu.getUnitId() != id) return MODBUS_RTU_MASTER_UNEXPECTED_ID; - if (adu.getFunctionCode() == (functionCode + 0x80)) { - _exceptionResponse = adu.data[0]; - return MODBUS_RTU_MASTER_EXCEPTION_RESPONSE; - } - if (adu.getFunctionCode() != functionCode) return MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE; - if (adu.getDataLen() != 4) return MODBUS_RTU_MASTER_UNEXPECTED_LENGTH; - if (adu.getDataRegister(0) != address) return MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS; - if (adu.getDataRegister(2) != value) return MODBUS_RTU_MASTER_UNEXPECTED_VALUE; - return MODBUS_RTU_MASTER_SUCCESS; -} - - - -ModbusRTUMasterError Modbus::_translateCommError(ModbusRTUCommError commError) { - switch (commError) { - case MODBUS_RTU_COMM_SUCCESS: - return MODBUS_RTU_MASTER_SUCCESS; - case MODBUS_RTU_COMM_TIMEOUT: - return MODBUS_RTU_MASTER_RESPONSE_TIMEOUT; - case MODBUS_RTU_COMM_FRAME_ERROR: - return MODBUS_RTU_MASTER_FRAME_ERROR; - case MODBUS_RTU_COMM_CRC_ERROR: - return MODBUS_RTU_MASTER_CRC_ERROR; - case MODBUS_RTU_COMM_WAITING: - return MODBUS_RTU_MASTER_WAITING; - default: - return MODBUS_RTU_MASTER_UNKNOWN_COMM_ERROR; - } -} - /************************************************************ * Modbus implementation ************************************************************/ // Constructor for Modbus -Modbus::Modbus(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA, int waitB) : _rtuComm(serial, txPin) { +Modbus::Modbus(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA, int waitB) { _baud = baud; _serialD = &serial; _txPin = txPin; - _rtuComm.setTimeout(500); _busNo = busNo; _cycleTime = cycleTimeMS * 1000UL; // convert from milliseconds to microseconds. _waitA = waitA; @@ -529,40 +211,17 @@ void Modbus::_loop(unsigned long currentMicros) { if (_currentMicros - _cycleStartTime < _cycleTime) return; _cycleStartTime = _currentMicros; if (_currentNode == NULL) return; - const char* errorStrings[16] = { "success", "invalid id", "invalid buffer", "invalid quantity", "response timeout", "frame error", "crc error", "unknown comm error", "unexpected id", "exception response", "unexpected function code", "unexpected response length", "unexpected byte count", "unexpected address", "unexpected value", "unexpected quantity" }; bool flagOK = true; #if defined(MODBUS_STM_COMM) ArduinoPins::fastWriteDigital(MODBUS_STM_COMM,HIGH); #endif - uint8_t error; - // send reads and writes, DIAG on errors other than 0 (Success), or 3 (Invalid Quantity) - switch (_operationCount) { - case 0: - error = writeMultipleHoldingRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->holdingRegisters, _currentNode->getNumHoldingRegisters()); - if (error != MODBUS_RTU_MASTER_SUCCESS && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusHR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumHoldingRegisters(), errorStrings[error]); - if (error != MODBUS_RTU_MASTER_SUCCESS && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; - break; - case 1: - error = writeMultipleCoils(_currentNode->getNodeID(), 0, (int*) _currentNode->coils, _currentNode->getNumCoils()); - if (error != MODBUS_RTU_MASTER_SUCCESS && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusMC: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumCoils(), errorStrings[error]); - if (error != MODBUS_RTU_MASTER_SUCCESS && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; - break; - case 2: - error = readInputRegisters(_currentNode->getNodeID(), 0, (uint16_t*) _currentNode->inputRegisters, _currentNode->getNumInputRegisters()); - if (error != MODBUS_RTU_MASTER_SUCCESS && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusIR: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumInputRegisters(), errorStrings[error]); - if (error != MODBUS_RTU_MASTER_SUCCESS && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; - break; - case 3: - error = readDiscreteInputs(_currentNode->getNodeID(), 0, (int*) _currentNode->discreteInputs, _currentNode->getNumDiscreteInputs()); - if (error != MODBUS_RTU_MASTER_SUCCESS && error != MODBUS_RTU_MASTER_WAITING) DIAG(F("ModbusDI: T%d F%d N%d %s"), _currentNode->getNodeID(), 0, _currentNode->getNumDiscreteInputs(), errorStrings[error]); - if (error != MODBUS_RTU_MASTER_SUCCESS && (error != MODBUS_RTU_MASTER_WAITING || _waitCounter > 2)) flagOK = false; - break; - } + + + if (error == MODBUS_RTU_MASTER_WAITING) { if (_waitCounter > _waitA) { // retry after 10 cycles of waiting, or user setting waitA. - _resetWaiting(); // reset Waiting flag so it retries. _waitCounter = 0; _waitCounterB++; } else { @@ -585,7 +244,6 @@ void Modbus::_loop(unsigned long currentMicros) { _operationCount++; // improve error recovery... } else { _operationCount = 0; - _currentNode->procData(); _currentNode = _currentNode->getNext(); } } @@ -623,7 +281,8 @@ Modbusnode::Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID) _nPins = nPins; _busNo = busNo; _nodeID = nodeID; - + if (_nodeID > 255) _nodeID = 255; + // Add this device to HAL device list IODevice::addDevice(this); _display(); diff --git a/IO_Modbus.h b/IO_Modbus.h index 6eb2fbb6..d3a4da78 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -53,101 +53,9 @@ #define IO_MODBUS_H #include "IODevice.h" -class ModbusADU { - public: - uint8_t *rtu = _adu + 6; - uint8_t *tcp = _adu; - uint8_t *pdu = _adu + 7; - uint8_t *data = _adu + 8; - - 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(); - uint16_t getLength(); - 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 updateCrc(); - bool crcGood(); - - void prepareExceptionResponse(uint8_t exceptionCode); - - private: - uint8_t _adu[262]; - void _setRegister(uint8_t *buf, uint16_t index, uint16_t value); - uint16_t _getRegister(uint8_t *buf, uint16_t index); - uint16_t _calculateCrc(uint16_t len); - -}; - +#include uint16_t div8RndUp(uint16_t value); -enum ModbusRTUCommError : uint8_t { - MODBUS_RTU_COMM_SUCCESS = 0, - MODBUS_RTU_COMM_TIMEOUT = 1, - MODBUS_RTU_COMM_FRAME_ERROR = 2, - MODBUS_RTU_COMM_CRC_ERROR = 3, - MODBUS_RTU_COMM_WAITING = 4 -}; - -class ModbusRTUComm { - public: - ModbusRTUComm(Stream& serial, VPIN dePin = VPIN_NONE, VPIN rePin = VPIN_NONE); - void begin(unsigned long baud, uint32_t config = SERIAL_8N1); - void setTimeout(unsigned long timeout); - ModbusRTUCommError readAdu(ModbusADU& adu); - bool _waiting_for_read = false; - void writeAdu(ModbusADU& adu); - void clearRxBuffer(); - Stream& _serial; - VPIN _dePin; - VPIN _rePin; - private: - - unsigned long _charTimeout; - unsigned long _frameTimeout; - unsigned long _postDelay = 0UL; - unsigned long _readTimeout = 0UL; - unsigned long _startTimeout = 0UL; -}; - -enum ModbusRTUMasterError : uint8_t { - MODBUS_RTU_MASTER_SUCCESS = 0, - MODBUS_RTU_MASTER_INVALID_ID = 1, - MODBUS_RTU_MASTER_INVALID_BUFFER = 2, - MODBUS_RTU_MASTER_INVALID_QUANTITY = 3, - MODBUS_RTU_MASTER_RESPONSE_TIMEOUT = 4, - MODBUS_RTU_MASTER_FRAME_ERROR = 5, - MODBUS_RTU_MASTER_CRC_ERROR = 6, - MODBUS_RTU_MASTER_UNKNOWN_COMM_ERROR = 7, - MODBUS_RTU_MASTER_UNEXPECTED_ID = 8, - MODBUS_RTU_MASTER_EXCEPTION_RESPONSE = 9, - MODBUS_RTU_MASTER_UNEXPECTED_FUNCTION_CODE = 10, - MODBUS_RTU_MASTER_UNEXPECTED_LENGTH = 11, - MODBUS_RTU_MASTER_UNEXPECTED_BYTE_COUNT = 12, - MODBUS_RTU_MASTER_UNEXPECTED_ADDRESS = 13, - MODBUS_RTU_MASTER_UNEXPECTED_VALUE = 14, - MODBUS_RTU_MASTER_UNEXPECTED_QUANTITY = 15, - MODBUS_RTU_MASTER_WAITING = 16 -}; - /********************************************************************** * Modbusnode class @@ -185,7 +93,21 @@ class Modbusnode : public IODevice { int configBPinsPU[16]; int configAPinsO[16]; int configAPinsI[16]; - + // EX-IOExpander protocol flags + enum { + EXIOINIT = 0xE0, // Flag to initialise setup procedure + EXIORDY = 0xE1, // Flag we have completed setup procedure, also for EX-IO to ACK setup + EXIODPUP = 0xE2, // Flag we're sending digital pin pullup configuration + EXIOVER = 0xE3, // Flag to get version + EXIORDAN = 0xE4, // Flag to read an analogue input + EXIOWRD = 0xE5, // Flag for digital write + EXIORDD = 0xE6, // Flag to read digital input + EXIOENAN = 0xE7, // Flag to enable an analogue pin + EXIOINITA = 0xE8, // Flag we're receiving analogue pin mappings + EXIOPINS = 0xE9, // Flag we're receiving pin counts for buffers + EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM) + EXIOERR = 0xEF, // Flag we've received an error + }; void resetInit() { for (int i = 0; i < 16; i++) { capePinsBI[i] = 0; @@ -225,6 +147,32 @@ class Modbusnode : public IODevice { } public: + enum ProfileType : int { + Instant = 0, // Moves immediately between positions (if duration not specified) + UseDuration = 0, // Use specified duration + Fast = 1, // Takes around 500ms end-to-end + Medium = 2, // 1 second end-to-end + Slow = 3, // 2 seconds end-to-end + Bounce = 4, // For semaphores/turnouts with a bit of bounce!! + NoPowerOff = 0x80, // Flag to be ORed in to suppress power off after move. + }; + + uint8_t _numDigitalPins = 0; + uint8_t _numAnaloguePins = 0; + + uint8_t _majorVer = 0; + uint8_t _minorVer = 0; + uint8_t _patchVer = 0; + + uint8_t* _digitalInputStates = NULL; + uint8_t* _analogueInputStates = NULL; + uint8_t* _analogueInputBuffer = NULL; // buffer for I2C input transfers + uint8_t _readCommandBuffer[1]; + + 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; + I2CRB _i2crb; static void create(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID) { if (checkNoOverlap(firstVpin, nPins)) new Modbusnode(firstVpin, nPins, busNo, nodeID); } @@ -314,96 +262,173 @@ class Modbusnode : public IODevice { } bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override { - byte arduinoPin = params[1]; if (paramCount != 1) return false; int pin = vpin - _firstVpin; if (configType == CONFIGURE_INPUT) { - bool inputPullup = false; - if (params[2] == 1) inputPullup = true; - bool status = addPinBI(vpin,inputPullup); - if (status == false) { - return true; - } else - DIAG(F("IO_Modbus Vpin %u, Arduino Pin %d, cannot be used as a digital input pin"), (int)vpin, arduinoPin); - } else if (configType == CONFIGURE_OUTPUT) { - bool status = addPinBO(vpin); - if (status == false) { - return true; - } else - DIAG(F("IO_Modbus Vpin %u, Arduino Pin %d, cannot be used as a digital Output pin"), (int)vpin, arduinoPin); - } else if (configType == CONFIGURE_SERVO) { - //blah - } else if (configType == CONFIGURE_ANALOGOUTPUT) { - bool status = addPinAO(vpin); - if (status == false) { - return true; - } else - DIAG(F("IO_Modbus Vpin %u, Arduino Pin %d, cannot be used as a analog Output pin"), (int)vpin, arduinoPin); + Modbus* mb = Modbus::findBus(0); + mb->_CommMode = 2; + mb->_pullup = params[0]; + mb->_pin = pin; + mb->_opperation = 1; } else if (configType == CONFIGURE_ANALOGINPUT) { - bool status = addPinAI(vpin); - if (status == false) { - return true; - } else - DIAG(F("IO_Modbus Vpin %u, Arduino Pin %d, cannot be used as a analog Input pin"), (int)vpin, arduinoPin); + // TODO: Consider moving code from _configureAnalogIn() to here and remove _configureAnalogIn + // from IODevice class definition. Not urgent, but each virtual function defined + // means increasing the RAM requirement of every HAL device driver, whether it's relevant + // to the driver or not. + return false; } return false; } - void _begin() override { - resetInit(); - coils[0] = (int*) 1; // set config mode - coils[1] = (int*) 1; // set pull capabilities + int _configureAnalogIn(VPIN vpin) override { + int pin = vpin - _firstVpin; + Modbus* mb = Modbus::findBus(0); + mb->_CommMode = 2; + mb->_pin = pin; + mb ->_opperation = 2; - _initialised = false; + return false; } - - - void procData() { - if (isInitialised()) { // read/write data - for (int i = 0; i < 16; i++) { - holdingRegisters[i] = (uint16_t*) dataBO[i]; - dataBI[i] = (int) inputRegisters[i]; - } - for (int i = 16; i < 84; i++) { - holdingRegisters[i] = (uint16_t*) dataAO[i]; - dataAI[i] = (int) inputRegisters[i]; + void _begin() override { + Modbus* mb = Modbus::findBus(0); + if (mb->_txPin != VPIN_NONE) { + pinMode(mb->_txPin, OUTPUT); + ArduinoPins::fastWriteDigital(mb->_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)); + 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); + unsigned long startMillis = millis(); + while (!mb->_serialD->available()) { + if (millis() - startMillis >= 500) return; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (mb->_serialD->available()) { + startMicros = micros(); + receiveBuffer[len] = mb->_serialD->read(); + len++; } - } else { // read/write config - if (discreteInputs[0] == (int*) 1 && discreteInputs[1] == (int*) 1){ // get capabilities - for (int i = 0; i < 16; i++) { // read capabilities params - capePinsBI[i] = (int) inputRegisters[i]; - capePinsBO[i] = (int) inputRegisters[i+16]; - capePinsPU[i] = (int) inputRegisters[i+32]; - capePinsAI[i] = (int) inputRegisters[i+48]; - capePinsAO[i] = (int) inputRegisters[i+64]; + } while (micros() - startMicros <= 500 && len < 256); + if (receiveBuffer[0] == EXIOPINS && mb->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { + _numDigitalPins = receiveBuffer[1]; + _numAnaloguePins = receiveBuffer[2]; + + // See if we already have suitable buffers assigned + if (_numDigitalPins>0) { + size_t digitalBytesNeeded = (_numDigitalPins + 7) / 8; + if (_digitalPinBytes < digitalBytesNeeded) { + // Not enough space, free any existing buffer and allocate a new one + if (_digitalPinBytes > 0) free(_digitalInputStates); + if ((_digitalInputStates = (byte*) calloc(digitalBytesNeeded, 1)) != NULL) { + _digitalPinBytes = digitalBytesNeeded; + } else { + DIAG(F("EX-IOExpanderMB node:%d ERROR alloc %d bytes"), _nodeID, digitalBytesNeeded); + _deviceState = DEVSTATE_FAILED; + _digitalPinBytes = 0; + return; + } } - coils[0] = (int*) 1; // config mode - coils[1] = (int*) 0; // exit cape mode - for (int i = 0; i < 16; i++) { // load config params - holdingRegisters[i] = (uint16_t*) configBPinsI[i]; - holdingRegisters[i+16] = (uint16_t*) configBPinsO[i]; - holdingRegisters[i+32] = (uint16_t*) configBPinsPU[i]; - holdingRegisters[i+48] = (uint16_t*) configAPinsI[i]; - holdingRegisters[i+64] = (uint16_t*) configAPinsO[i]; - } - } else if (discreteInputs[0] == (int*) 1 && discreteInputs[1] == (int*) 0) { - for (int i = 0; i < 16; i++) { - if (configBPinsI[i] != (int) inputRegisters[i]) spitError(i); - if (configBPinsO[i] != (int) inputRegisters[i+16]) spitError(i+16); - if (configBPinsPU[i] != (int) inputRegisters[i+32]) spitError(i+32); - if (configAPinsI[i] != (int) inputRegisters[i+48]) spitError(i+48); - if (configAPinsO[i] != (int) inputRegisters[i+64]) spitError(i+64); + } + + if (_numAnaloguePins>0) { + size_t analogueBytesNeeded = _numAnaloguePins * 2; + if (_analoguePinBytes < analogueBytesNeeded) { + // Free any existing buffers and allocate new ones. + if (_analoguePinBytes > 0) { + free(_analogueInputBuffer); + free(_analogueInputStates); + free(_analoguePinMap); + } + _analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1); + _analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1); + _analoguePinMap = (uint8_t*) calloc(_numAnaloguePins, 1); + if (_analogueInputStates != NULL && + _analogueInputBuffer != NULL && + _analoguePinMap != NULL) { + _analoguePinBytes = analogueBytesNeeded; + } else { + DIAG(F("EX-IOExpanderMB node:%d ERROR alloc analog pin bytes"), _nodeID); + _deviceState = DEVSTATE_FAILED; + _analoguePinBytes = 0; + return; + } } - // todo error checking and set failure mode - // for now, assume everything is fine, sort this out later if needed - coils[0] = (int*) 0; // exit config mode - coils[1] = (int*) 0; // not in cape mode - setInitialised(); } + } else { + DIAG(F("EX-IOExpanderMB node:%d ERROR configuring device (CRC: %s)"), _nodeID, mb->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)? "PASS":"FAIL"); + _deviceState = DEVSTATE_FAILED; + return; + } + commandBuffer[0] = EXIOINITA; + mb->updateCrc(commandBuffer,sizeof(commandBuffer)); + 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); + startMillis = millis(); + while (!mb->_serialD->available()) { + if (millis() - startMillis >= 500) return; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (mb->_serialD->available()) { + startMicros = micros(); + receiveBuffer[len] = mb->_serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + if (mb->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)); + 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); + startMillis = millis(); + while (!mb->_serialD->available()) { + if (millis() - startMillis >= 500) return; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (mb->_serialD->available()) { + startMicros = micros(); + versionBuffer[len] = mb->_serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + if (mb->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); } + + + + + + +#ifdef DIAG_IO + _display(); +#endif + _initialised = false; } + int _read(VPIN vpin) override { // Return current state from this device uint16_t pin = vpin - _firstVpin; @@ -487,6 +512,8 @@ class Modbusnode : public IODevice { getNumAnalogOutputsVPINsMin(), getNumAnalogOutputsVPINsMax()); } + + }; /********************************************************************** @@ -501,9 +528,12 @@ class Modbus : 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; - int8_t _txPin; + Modbusnode *_nodeListStart = NULL, *_nodeListEnd = NULL; Modbusnode *_currentNode = NULL; uint8_t _exceptionResponse = 0; @@ -519,28 +549,117 @@ class Modbus : public IODevice { 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; + static Modbus *_busList; // linked list of defined bus instances - ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, int buf[], uint16_t quantity); - ModbusRTUMasterError _readValues(uint8_t id, uint8_t functionCode, uint16_t startAddress, uint16_t buf[], uint16_t quantity); - ModbusRTUMasterError _writeSingleValue(uint8_t id, uint8_t functionCode, uint16_t address, uint16_t value); + int _waitCounter = 0; int _waitCounterB = 0; int _waitA; int _waitB; - - void _resetWaiting() { - _rtuComm._waiting_for_read = false; +// 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; } + + + unsigned long _charTimeout; + unsigned long _frameTimeout; + enum {RDS_IDLE, RDS_DIGITAL, RDS_ANALOGUE}; // Read operation states + uint8_t _readState = RDS_IDLE; + + unsigned long _lastDigitalRead = 0; + 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 + + // EX-IOExpander protocol flags + enum { + EXIOINIT = 0xE0, // Flag to initialise setup procedure + EXIORDY = 0xE1, // Flag we have completed setup procedure, also for EX-IO to ACK setup + EXIODPUP = 0xE2, // Flag we're sending digital pin pullup configuration + EXIOVER = 0xE3, // Flag to get version + EXIORDAN = 0xE4, // Flag to read an analogue input + EXIOWRD = 0xE5, // Flag for digital write + EXIORDD = 0xE6, // Flag to read digital input + EXIOENAN = 0xE7, // Flag to enable an analogue pin + EXIOINITA = 0xE8, // Flag we're receiving analogue pin mappings + EXIOPINS = 0xE9, // Flag we're receiving pin counts for buffers + EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM) + EXIOERR = 0xEF, // Flag we've received an error + }; + int tasks[255][25]; + int taskCnt = 0; public: + void addTask(int taskNum, int paranCnt, int *param[]) { + tasks[taskCnt][0] = taskNum; + switch(taskNum) { + case 0: // configure pin + tasks[taskNum][1] = param[0]; // pin + tasks[taskNum][2] = param[1]; // configtype + tasks[taskNum][3] = param[2]; // paramcount + for (int i=0; i < param[2]; i++) { + tasks[taskNum][i+4] = param[i+3]; // params + } + break; + case 1: // configure analog in + tasks[taskNum][1] = param[0]; // pin + break; + + + } + } + + 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 Modbus(busNo, serial, baud, cycleTimeMS, txPin, waitA, waitB); } HardwareSerial *_serialD; - ModbusRTUComm _rtuComm; // Device-specific initialisation void _begin() override { _serialD->begin(_baud, SERIAL_8N1); - _rtuComm.begin(_baud, SERIAL_8N1); + unsigned long bitsPerChar = 10; + if (_baud <= 19200) { + _charTimeout = (bitsPerChar * 2500000) / _baud; + _frameTimeout = (bitsPerChar * 4500000) / _baud; + } + else { + _charTimeout = (bitsPerChar * 1000000) / _baud + 750; + _frameTimeout = (bitsPerChar * 1000000) / _baud + 1750; + } + clearRxBuffer(); #if defined(MODBUS_STM_OK) pinMode(MODBUS_STM_OK, OUTPUT); ArduinoPins::fastWriteDigital(MODBUS_STM_OK,LOW); @@ -558,16 +677,11 @@ class Modbus : public IODevice { _display(); #endif } - ModbusRTUMasterError _translateCommError(ModbusRTUCommError commError); - ModbusRTUMasterError readCoils(uint8_t id, uint16_t startAddress, int buf[], uint16_t quantity); - ModbusRTUMasterError readDiscreteInputs(uint8_t id, uint16_t startAddress, int buf[], uint16_t quantity); - ModbusRTUMasterError readHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); - ModbusRTUMasterError readInputRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); + int _CommMode = 0; + int _opperation = 0; + uint16_t _pullup; + uint16_t _pin; - ModbusRTUMasterError writeSingleCoil(uint8_t id, uint16_t address, int value); - ModbusRTUMasterError writeSingleHoldingRegister(uint8_t id, uint16_t address, uint16_t value); - ModbusRTUMasterError writeMultipleCoils(uint8_t id, uint16_t startAddress, int buf[], uint16_t quantity); - ModbusRTUMasterError writeMultipleHoldingRegisters(uint8_t id, uint16_t startAddress, uint16_t buf[], uint16_t quantity); // Loop function (overriding IODevice::_loop(unsigned long)) void _loop(unsigned long currentMicros) override; From d105e0c6070df1cd40876d73974c5e0995cc0293 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Thu, 12 Dec 2024 12:17:23 -0500 Subject: [PATCH 39/73] save current --- IO_Modbus.cpp | 100 +++++++++++++++++++++++++++++++++++++++++++++++++- IO_Modbus.h | 71 ++++++++++++++++++++++++++--------- 2 files changed, 151 insertions(+), 20 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 490992a9..c7fd24c6 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -211,14 +211,110 @@ void Modbus::_loop(unsigned long currentMicros) { if (_currentMicros - _cycleStartTime < _cycleTime) return; _cycleStartTime = _currentMicros; if (_currentNode == NULL) return; - const char* errorStrings[16] = { "success", "invalid id", "invalid buffer", "invalid quantity", "response timeout", "frame error", "crc error", "unknown comm error", "unexpected id", "exception response", "unexpected function code", "unexpected response length", "unexpected byte count", "unexpected address", "unexpected value", "unexpected quantity" }; - + bool flagOK = true; #if defined(MODBUS_STM_COMM) ArduinoPins::fastWriteDigital(MODBUS_STM_COMM,HIGH); #endif +if (taskCnt > 0) { + // run through tasks + int* taskData[25]; + getNextTask(taskData); + switch((int) taskData[0]) { + case 0: + // protection for pulling empty task + break; + case 1: // configure pin + if (taskData[4] == (int*) CONFIGURE_INPUT) { + 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); + if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); + _serialD->write(outBuffer, sizeof(outBuffer)); + _serialD->flush(); + if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, LOW); + unsigned long startMillis = millis(); + if (!_serialD->available()) { + if (waitReceive == true && _waitCounter > _waitA) { + + } + if (millis() - startMillis >= 500) return; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (_serialD->available()) { + startMicros = micros(); + responseBuffer[len] = _serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + 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[2]); + } + } + } 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 + // means increasing the RAM requirement of every HAL device driver, whether it's relevant + // to the driver or not. + } + break; + 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); + if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); + _serialD->write(commandBuffer, sizeof(commandBuffer)); + _serialD->flush(); + if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, LOW); + unsigned long startMillis = millis(); + if (!_serialD->available()) { + if (waitReceive == true && _waitCounter > _waitA) { + + } + if (millis() - startMillis >= 500) return; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (_serialD->available()) { + startMicros = micros(); + responseBuffer[len] = _serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + + 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[2], (int) taskData[0]); + } + } + break; + case 3: // write pin + uint8_t digitalOutBuffer[6]; + uint8_t responseBuffer[3]; + digitalOutBuffer[0] = EXIOWRD; + digitalOutBuffer[1] = (uint8_t) taskData[0]; + digitalOutBuffer[2] = (uint8_t) taskData[3]; + digitalOutBuffer[3] = value; + uint8_t status = I2CManager.read(_I2CAddress, responseBuffer, 1, digitalOutBuffer, 3); + if (status != I2C_STATUS_OK) { + reportError(status); + } else { + if (responseBuffer[0] != EXIORDY) { + DIAG(F("Vpin %u cannot be used as a digital output pin"), (int)vpin); + } + } + } +} else { + // receive states +} if (error == MODBUS_RTU_MASTER_WAITING) { if (_waitCounter > _waitA) { // retry after 10 cycles of waiting, or user setting waitA. diff --git a/IO_Modbus.h b/IO_Modbus.h index d3a4da78..790767d1 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -53,7 +53,6 @@ #define IO_MODBUS_H #include "IODevice.h" -#include uint16_t div8RndUp(uint16_t value); @@ -298,7 +297,7 @@ class Modbusnode : public IODevice { } 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)); + 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(); @@ -367,7 +366,7 @@ class Modbusnode : public IODevice { return; } commandBuffer[0] = EXIOINITA; - mb->updateCrc(commandBuffer,sizeof(commandBuffer)); + 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(); @@ -392,7 +391,7 @@ class Modbusnode : public IODevice { } uint8_t versionBuffer[5]; commandBuffer[0] = EXIOVER; - mb->updateCrc(commandBuffer,sizeof(commandBuffer)); + 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(); @@ -551,7 +550,7 @@ class Modbus : public IODevice { int _operationCount = 0; static Modbus *_busList; // linked list of defined bus instances - + bool waitReceive = false; int _waitCounter = 0; int _waitCounterB = 0; int _waitA; @@ -590,25 +589,61 @@ class Modbus : public IODevice { EXIOERR = 0xEF, // Flag we've received an error }; int tasks[255][25]; - int taskCnt = 0; + + void _moveTasks() { + // used one in lead, so move forward + for (int i = 0; i < taskCnt-1; i++) { + for (int j = 0; j < 25; j++) { + tasks[i][j] = tasks[i+1][j+1]; + } + } + taskCnt--; + } public: - void addTask(int taskNum, int paranCnt, int *param[]) { - tasks[taskCnt][0] = taskNum; + int taskCnt = 0; + void addTask(int nodeID, int taskNum, int paramCnt, int *param[]) { + taskCnt++; + tasks[taskCnt][0] = nodeID; + tasks[taskCnt][1] = taskNum; + tasks[taskCnt][2] = paramCnt; switch(taskNum) { - case 0: // configure pin - tasks[taskNum][1] = param[0]; // pin - tasks[taskNum][2] = param[1]; // configtype - tasks[taskNum][3] = param[2]; // paramcount - for (int i=0; i < param[2]; i++) { - tasks[taskNum][i+4] = param[i+3]; // params + case 0: + // empty task + case 1: // configure pin + tasks[taskCnt][3] = (int) param[0]; // pin + tasks[taskCnt][4] = (int) param[1]; // configtype + tasks[taskCnt][5] = (int) param[2]; // paramcount + for (int i=0; i < (int) param[2]; i++) { + tasks[taskCnt][i+6] = (int) param[i+3]; // params } break; - case 1: // configure analog in - tasks[taskNum][1] = param[0]; // pin + case 2: // configure analog in + tasks[taskCnt][3] = (int) param[0]; // pin break; - - + case 3: // write pin + tasks[taskCnt][3] = (int) param[0]; // pin + tasks[taskCnt][4] = (int) param[1]; // value + break; + case 4: // write analog + tasks[taskCnt][3] = (int) param[0]; // pin + tasks[taskCnt][4] = (int) param[1]; // value + tasks[taskCnt][5] = (int) param[2]; // profile + tasks[taskCnt][6] = (int) param[3]; // duration + break; + } + } + + int getNextTask(int *buf[]) { + int paramCnt = 0; + for (int i = 0; i < 25; i++) { + if (i == 0) buf[i] = (int*) tasks[0][i]; // NodeID + if (i == 1) buf[i] = (int*) tasks[0][i]; // tasknum + else if (i == 2) paramCnt = tasks[0][i]; // paramcnt + else { + buf[i-1] = (int*) tasks[0][i]; + } } + _moveTasks(); } int8_t _txPin; From 523f2bd7ea09015e25c79d63cf4750f4d30d002d Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Thu, 12 Dec 2024 14:52:51 -0500 Subject: [PATCH 40/73] first untested prototype --- IO_Modbus.cpp | 285 ++++++++++++++++++++++++++++++++++++++++---------- IO_Modbus.h | 40 +++++++ 2 files changed, 267 insertions(+), 58 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index c7fd24c6..a8a20e62 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -21,7 +21,51 @@ #include "IO_Modbus.h" #include "defines.h" +uint8_t MBRB::wait() { + while (status==MB_STATUS_PENDING) { + // may as well whistle or something + }; + return status; +} +bool MBRB::isBusy() { + if (status==MB_STATUS_PENDING) { + return true; + } else + return false; +} +void MBRB::setReadParams(int nodeID, uint8_t *readBuffer, uint8_t readLen) { + this->nodeID = nodeID; + this->writeLen = 0; + this->readBuffer = readBuffer; + this->readLen = readLen; + this->operation = OPERATION_READ; + this->status = MB_STATUS_OK; +} +void MBRB::setRequestParams(int nodeID, uint8_t *readBuffer, uint8_t readLen, + const uint8_t *writeBuffer, uint8_t writeLen) { + this->nodeID = nodeID; + this->writeBuffer = writeBuffer; + this->writeLen = writeLen; + this->readBuffer = readBuffer; + this->readLen = readLen; + this->operation = OPERATION_REQUEST; + this->status = MB_STATUS_OK; +} +void MBRB::setWriteParams(int nodeID, const uint8_t *writeBuffer, uint8_t writeLen) { + this->nodeID = nodeID; + this->writeBuffer = writeBuffer; + this->writeLen = writeLen; + this->readLen = 0; + this->operation = OPERATION_SEND; + this->status = MB_STATUS_OK; +} +void MBRB::suppressRetries(bool suppress) { + if (suppress) + this->operation |= OPERATION_NORETRY; + else + this->operation &= ~OPERATION_NORETRY; +} void Modbus::setTransactionId(uint16_t transactionId) { _setRegister(tcp, 0, transactionId); } @@ -231,17 +275,19 @@ if (taskCnt > 0) { uint8_t outBuffer[6] = {EXIODPUP, (uint8_t) taskData[0], (uint8_t)taskData[3], pullup}; uint8_t responseBuffer[3]; updateCrc(outBuffer,sizeof(outBuffer)-2); - if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); - _serialD->write(outBuffer, sizeof(outBuffer)); - _serialD->flush(); - if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, LOW); + if (waitReceive == false) { + if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); + _serialD->write(outBuffer, sizeof(outBuffer)); + _serialD->flush(); + if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, LOW); + } unsigned long startMillis = millis(); if (!_serialD->available()) { if (waitReceive == true && _waitCounter > _waitA) { - - } - if (millis() - startMillis >= 500) return; + flagOK = false; + } else waitReceive = true; } + waitReceive = false; uint16_t len = 0; unsigned long startMicros = micros(); do { @@ -254,9 +300,9 @@ 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[2]); + DIAG(F("EXIOMB 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 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,32 +314,33 @@ if (taskCnt > 0) { uint8_t commandBuffer[5] = {EXIOENAN, (uint8_t) taskData[0], (uint8_t) taskData[3]}; uint8_t responseBuffer[3]; updateCrc(commandBuffer,sizeof(commandBuffer)-2); - if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); - _serialD->write(commandBuffer, sizeof(commandBuffer)); - _serialD->flush(); - if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, LOW); + if (waitReceive == false) { + if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); + _serialD->write(commandBuffer, sizeof(commandBuffer)); + _serialD->flush(); + if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, LOW); + } unsigned long startMillis = millis(); if (!_serialD->available()) { if (waitReceive == true && _waitCounter > _waitA) { - - } - if (millis() - startMillis >= 500) return; + flagOK = false; + } else waitReceive = true; } - uint16_t len = 0; - unsigned long startMicros = micros(); - do { - if (_serialD->available()) { - startMicros = micros(); - responseBuffer[len] = _serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (_serialD->available()) { + startMicros = micros(); + responseBuffer[len] = _serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); 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[2], (int) taskData[0]); + DIAG(F("EX-IOExpanderMB: Vpin %u on node %d cannot be used as an analogue input pin"), (int) taskData[3], (int) taskData[0]); } - } + } else DIAG(F("EXIOMB node %d CRC Error"), (int) taskData[0]); break; case 3: // write pin uint8_t digitalOutBuffer[6]; @@ -301,48 +348,170 @@ if (taskCnt > 0) { digitalOutBuffer[0] = EXIOWRD; digitalOutBuffer[1] = (uint8_t) taskData[0]; digitalOutBuffer[2] = (uint8_t) taskData[3]; - digitalOutBuffer[3] = value; - uint8_t status = I2CManager.read(_I2CAddress, responseBuffer, 1, digitalOutBuffer, 3); - if (status != I2C_STATUS_OK) { - reportError(status); + digitalOutBuffer[3] = (uint8_t) taskData[4]; + updateCrc(digitalOutBuffer,sizeof(digitalOutBuffer)-2); + if (waitReceive == false) { + if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); + _serialD->write(digitalOutBuffer, sizeof(digitalOutBuffer)); + _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(); + responseBuffer[len] = _serialD->read(); + len++; + } + } 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]); + } + } 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"), + vpin, value, profile, duration, _deviceState == DEVSTATE_FAILED?F("DEVSTATE_FAILED"):F("")); +#endif + servoBuffer[0] = EXIOWRAN; + servoBuffer[1] = (uint8_t) taskData[0]; + servoBuffer[2] = (uint8_t) taskData[3]; + servoBuffer[3] = (uint8_t) taskData[4] & 0xFF; + servoBuffer[4] = (uint8_t) taskData[4] >> 8; + 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); + if (waitReceive == false) { + if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); + _serialD->write(servoBuffer, sizeof(servoBuffer)); + _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(); + responseBuffer[len] = _serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + if (!crcGood(responseBuffer,sizeof(responseBuffer)-2)) { + DIAG(F("EXIOMB node %d CRC Error"), (int) taskData[0]); + _deviceState = DEVSTATE_FAILED; } else { if (responseBuffer[0] != EXIORDY) { - DIAG(F("Vpin %u cannot be used as a digital output pin"), (int)vpin); + DIAG(F("Vpin %u cannot be used as a servo/PWM pin"), (int) taskData[3]); } } } } else { // receive states + if (_readState != RDS_IDLE) { + if (_mbrb.isBusy()) return; // If I2C operation still in progress, return + + uint8_t status = _mbrb.status; + if (status == I2C_STATUS_OK) { // If device request ok, read input data + + // First check if we need to process received data + if (_readState == RDS_ANALOGUE) { + // Read of analogue values was in progress, so process received values + // Here we need to copy the values from input buffer to the analogue value array. We need to + // do this to avoid tearing of the values (i.e. one byte of a two-byte value being changed + // while the value is being read). + memcpy(_currentNode->_analogueInputStates, _currentNode->_analogueInputBuffer, _currentNode->_analoguePinBytes); // Copy I2C input buffer to states + + } else if (_readState == RDS_DIGITAL) { + // Read of digital states was in progress, so process received values + // The received digital states are placed directly into the digital buffer on receipt, + // so don't need any further processing at this point (unless we want to check for + // changes and notify them to subscribers, to avoid the need for polling - see IO_GPIOBase.h). + } + } else + reportError(status, false); // report eror but don't go offline. -} - - if (error == MODBUS_RTU_MASTER_WAITING) { - if (_waitCounter > _waitA) { // retry after 10 cycles of waiting, or user setting waitA. - _waitCounter = 0; - _waitCounterB++; - } else { - _waitCounter++; + _readState = RDS_IDLE; + } + 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); } - if (_waitCounterB > _waitB) { // move on to next node if fails 10 times, or user setting waitB. - _waitCounter = 0; - _waitCounterB = 0; - _operationCount = 0; - - _currentNode = _currentNode->getNext(); + unsigned long startMillis = millis(); + if (!_serialD->available()) { + if (waitReceive == true && _waitCounter > _waitA) { + flagOK = false; + } else waitReceive = true; } - } else { - _waitCounter = 0; - _waitCounterB = 0; - } - - if (error == MODBUS_RTU_MASTER_SUCCESS) { // should have the effect of retrying same opperation until success - if (_operationCount < 3) { // unless it fails waitB and moves on to next node. may even - _operationCount++; // improve error recovery... - } else { - _operationCount = 0; - _currentNode = _currentNode->getNext(); + 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(); +} + #if defined(MODBUS_STM_OK) if (flagOK == true) { ArduinoPins::fastWriteDigital(MODBUS_STM_OK,HIGH); diff --git a/IO_Modbus.h b/IO_Modbus.h index 790767d1..ad23d54d 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -55,6 +55,45 @@ #include "IODevice.h" uint16_t div8RndUp(uint16_t value); +// Class defining a request context for an I2C operation. +class MBRB { +public: + volatile uint8_t status; // Completion status, or pending flag (updated from IRC) + volatile uint8_t nBytes; // Number of bytes read (updated from IRC) + + inline MBRB() { status = I2C_STATUS_OK; }; + uint8_t wait(); + bool isBusy(); + + void setReadParams(int nodeID, uint8_t *readBuffer, uint8_t readLen); + void setRequestParams(int nodeID, uint8_t *readBuffer, uint8_t readLen, const uint8_t *writeBuffer, uint8_t writeLen); + void setWriteParams(int nodeID, const uint8_t *writeBuffer, uint8_t writeLen); + void suppressRetries(bool suppress); + + uint8_t writeLen; + uint8_t readLen; + uint8_t operation; + int nodeID; + uint8_t *readBuffer; + const uint8_t *writeBuffer; + MBRB *nextRequest; +}; + +enum : uint8_t { + // Codes used by Wire and by native drivers + MB_STATUS_OK=0, + MB_STATUS_TRUNCATED=1, + MB_STATUS_NEGATIVE_ACKNOWLEDGE=2, + MB_STATUS_TRANSMIT_ERROR=3, + MB_STATUS_TIMEOUT=5, + // Code used by Wire only + MB_STATUS_OTHER_TWI_ERROR=4, // catch-all error + // Codes used by native drivers only + MB_STATUS_ARBITRATION_LOST=6, + MB_STATUS_BUS_ERROR=7, + MB_STATUS_UNEXPECTED_ERROR=8, + MB_STATUS_PENDING=253, +}; /********************************************************************** * Modbusnode class @@ -572,6 +611,7 @@ class Modbus : public IODevice { 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 + MBRB _mbrb; // EX-IOExpander protocol flags enum { From cd1afaf04d1f73224b9c177fb7e3e0232902e2c6 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Thu, 12 Dec 2024 15:22:07 -0500 Subject: [PATCH 41/73] now it is first prototype - forgot a few things --- IO_Modbus.cpp | 71 +--------------------------- IO_Modbus.h | 126 ++++++++++++++++---------------------------------- 2 files changed, 41 insertions(+), 156 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index a8a20e62..17ce6a7c 100644 --- a/IO_Modbus.cpp +++ b/IO_Modbus.cpp @@ -21,51 +21,6 @@ #include "IO_Modbus.h" #include "defines.h" -uint8_t MBRB::wait() { - while (status==MB_STATUS_PENDING) { - // may as well whistle or something - }; - return status; -} -bool MBRB::isBusy() { - if (status==MB_STATUS_PENDING) { - return true; - } else - return false; -} -void MBRB::setReadParams(int nodeID, uint8_t *readBuffer, uint8_t readLen) { - this->nodeID = nodeID; - this->writeLen = 0; - this->readBuffer = readBuffer; - this->readLen = readLen; - this->operation = OPERATION_READ; - this->status = MB_STATUS_OK; -} - -void MBRB::setRequestParams(int nodeID, uint8_t *readBuffer, uint8_t readLen, - const uint8_t *writeBuffer, uint8_t writeLen) { - this->nodeID = nodeID; - this->writeBuffer = writeBuffer; - this->writeLen = writeLen; - this->readBuffer = readBuffer; - this->readLen = readLen; - this->operation = OPERATION_REQUEST; - this->status = MB_STATUS_OK; -} -void MBRB::setWriteParams(int nodeID, const uint8_t *writeBuffer, uint8_t writeLen) { - this->nodeID = nodeID; - this->writeBuffer = writeBuffer; - this->writeLen = writeLen; - this->readLen = 0; - this->operation = OPERATION_SEND; - this->status = MB_STATUS_OK; -} -void MBRB::suppressRetries(bool suppress) { - if (suppress) - this->operation |= OPERATION_NORETRY; - else - this->operation &= ~OPERATION_NORETRY; -} void Modbus::setTransactionId(uint16_t transactionId) { _setRegister(tcp, 0, transactionId); } @@ -424,32 +379,8 @@ if (taskCnt > 0) { } } } else { - // receive states - if (_readState != RDS_IDLE) { - if (_mbrb.isBusy()) return; // If I2C operation still in progress, return - - uint8_t status = _mbrb.status; - if (status == I2C_STATUS_OK) { // If device request ok, read input data - - // First check if we need to process received data - if (_readState == RDS_ANALOGUE) { - // Read of analogue values was in progress, so process received values - // Here we need to copy the values from input buffer to the analogue value array. We need to - // do this to avoid tearing of the values (i.e. one byte of a two-byte value being changed - // while the value is being read). - memcpy(_currentNode->_analogueInputStates, _currentNode->_analogueInputBuffer, _currentNode->_analoguePinBytes); // Copy I2C input buffer to states - - } else if (_readState == RDS_DIGITAL) { - // Read of digital states was in progress, so process received values - // The received digital states are placed directly into the digital buffer on receipt, - // so don't need any further processing at this point (unless we want to check for - // changes and notify them to subscribers, to avoid the need for polling - see IO_GPIOBase.h). - } - } else - reportError(status, false); // report eror but don't go offline. + memcpy(_currentNode->_analogueInputStates, _currentNode->_analogueInputBuffer, _currentNode->_analoguePinBytes); // Copy I2C input buffer to states - _readState = RDS_IDLE; - } 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). diff --git a/IO_Modbus.h b/IO_Modbus.h index ad23d54d..27f4e160 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -55,46 +55,6 @@ #include "IODevice.h" uint16_t div8RndUp(uint16_t value); -// Class defining a request context for an I2C operation. -class MBRB { -public: - volatile uint8_t status; // Completion status, or pending flag (updated from IRC) - volatile uint8_t nBytes; // Number of bytes read (updated from IRC) - - inline MBRB() { status = I2C_STATUS_OK; }; - uint8_t wait(); - bool isBusy(); - - void setReadParams(int nodeID, uint8_t *readBuffer, uint8_t readLen); - void setRequestParams(int nodeID, uint8_t *readBuffer, uint8_t readLen, const uint8_t *writeBuffer, uint8_t writeLen); - void setWriteParams(int nodeID, const uint8_t *writeBuffer, uint8_t writeLen); - void suppressRetries(bool suppress); - - uint8_t writeLen; - uint8_t readLen; - uint8_t operation; - int nodeID; - uint8_t *readBuffer; - const uint8_t *writeBuffer; - MBRB *nextRequest; -}; - -enum : uint8_t { - // Codes used by Wire and by native drivers - MB_STATUS_OK=0, - MB_STATUS_TRUNCATED=1, - MB_STATUS_NEGATIVE_ACKNOWLEDGE=2, - MB_STATUS_TRANSMIT_ERROR=3, - MB_STATUS_TIMEOUT=5, - // Code used by Wire only - MB_STATUS_OTHER_TWI_ERROR=4, // catch-all error - // Codes used by native drivers only - MB_STATUS_ARBITRATION_LOST=6, - MB_STATUS_BUS_ERROR=7, - MB_STATUS_UNEXPECTED_ERROR=8, - MB_STATUS_PENDING=253, -}; - /********************************************************************** * Modbusnode class * @@ -210,7 +170,7 @@ class Modbusnode : public IODevice { 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; - I2CRB _i2crb; + static void create(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID) { if (checkNoOverlap(firstVpin, nPins)) new Modbusnode(firstVpin, nPins, busNo, nodeID); } @@ -302,29 +262,19 @@ class Modbusnode : public IODevice { bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override { if (paramCount != 1) return false; int pin = vpin - _firstVpin; - if (configType == CONFIGURE_INPUT) { - Modbus* mb = Modbus::findBus(0); - mb->_CommMode = 2; - mb->_pullup = params[0]; - mb->_pin = pin; - mb->_opperation = 1; - } else if (configType == CONFIGURE_ANALOGINPUT) { - // TODO: Consider moving code from _configureAnalogIn() to here and remove _configureAnalogIn - // from IODevice class definition. Not urgent, but each virtual function defined - // means increasing the RAM requirement of every HAL device driver, whether it's relevant - // to the driver or not. - return false; - } - return false; + int pin = vpin - _firstVpin; + Modbus* mb = Modbus::findBus(0); + int* param[] = {(int*)pin, (int*)configType, (int*)paramCount, (int*)params[0]}; + mb->addTask(_nodeID, 3, 4, param); + } int _configureAnalogIn(VPIN vpin) override { int pin = vpin - _firstVpin; Modbus* mb = Modbus::findBus(0); - mb->_CommMode = 2; - mb->_pin = pin; - mb ->_opperation = 2; - + int* params[] = {(int*)pin}; + mb->addTask(_nodeID, 3, 1, params); + return false; } @@ -468,40 +418,44 @@ class Modbusnode : public IODevice { int _read(VPIN vpin) override { - // Return current state from this device - uint16_t pin = vpin - _firstVpin; - int PinNum = pin / 16; - int PinBit = pin % 16; - if (bitRead(configAPinsI[PinNum],PinBit) == true) return bitRead(dataBI[PinNum],PinBit)? 1:0; - else return 0; + if (_deviceState == DEVSTATE_FAILED) return 0; + int pin = vpin - _firstVpin; + uint8_t pinByte = pin / 8; + bool value = bitRead(_digitalInputStates[pinByte], pin - pinByte * 8); + return value; } void _write(VPIN vpin, int value) override { - // Update current state for this device, in preparation the bus transmission - uint16_t pin = vpin - _firstVpin; - int PinNum = pin / 16; - int PinBit = pin % 16; - if (bitRead(configAPinsO[PinNum], PinBit) == true) { - if (value == 1) bitSet(dataBO[PinNum], PinBit); - else bitClear(dataBO[PinNum], PinBit); - } + if (_deviceState == DEVSTATE_FAILED) return; + int pin = vpin - _firstVpin; + Modbus* mb = Modbus::findBus(0); + int* params[] = {(int*)pin, (int*)value}; + mb->addTask(_nodeID, 3, 2, params); } - int _readAnalogue(VPIN vpin) { - // Return acquired data value, e.g. - uint16_t pin = vpin - _firstVpin; - int PinNum = pin / 16; - int PinBit = pin % 16; - if (bitRead(configAPinsI[PinNum],PinBit) == true) return dataAI[pin]; - else return 0; + int _readAnalogue(VPIN vpin) override { + if (_deviceState == DEVSTATE_FAILED) return 0; + int pin = vpin - _firstVpin; + for (uint8_t aPin = 0; aPin < _numAnaloguePins; aPin++) { + if (_analoguePinMap[aPin] == pin) { + uint8_t _pinLSBByte = aPin * 2; + uint8_t _pinMSBByte = _pinLSBByte + 1; + return (_analogueInputStates[_pinMSBByte] << 8) + _analogueInputStates[_pinLSBByte]; + } + } + return -1; // pin not found in table } - void _writeAnalogue(VPIN vpin, int value) { - uint16_t pin = vpin - _firstVpin; - int PinNum = pin / 16; - int PinBit = pin % 16; - if (bitRead(configAPinsI[PinNum],PinBit) == true) dataAO[pin] = value; + void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override { + uint8_t servoBuffer[7]; + uint8_t responseBuffer[1]; + + if (_deviceState == DEVSTATE_FAILED) return; + int pin = vpin - _firstVpin; + Modbus* mb = Modbus::findBus(0); + int* params[] = {(int*)pin, (int*)value, (int*)profile, (int*)duration}; + mb->addTask(_nodeID, 3, 4, params); } uint8_t getBusNumber() { @@ -611,7 +565,7 @@ class Modbus : public IODevice { 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 - MBRB _mbrb; + // EX-IOExpander protocol flags enum { From cd4230dafbd8b06cb6ea5df1532a83c0a2cc3506 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Thu, 12 Dec 2024 15:47:25 -0500 Subject: [PATCH 42/73] driver name change --- IO_Modbus.cpp => IO_RS485.cpp | 100 ++++++------- IO_Modbus.h => IO_RS485.h | 271 +++++++--------------------------- 2 files changed, 101 insertions(+), 270 deletions(-) rename IO_Modbus.cpp => IO_RS485.cpp (86%) rename IO_Modbus.h => IO_RS485.h (69%) diff --git a/IO_Modbus.cpp b/IO_RS485.cpp similarity index 86% rename from IO_Modbus.cpp rename to IO_RS485.cpp index 17ce6a7c..ade5013e 100644 --- a/IO_Modbus.cpp +++ b/IO_RS485.cpp @@ -18,101 +18,101 @@ * along with CommandStation. If not, see . */ -#include "IO_Modbus.h" +#include "IO_RS485.h" #include "defines.h" -void Modbus::setTransactionId(uint16_t transactionId) { +void RS485::setTransactionId(uint16_t transactionId) { _setRegister(tcp, 0, transactionId); } -void Modbus::setProtocolId(uint16_t protocolId) { +void RS485::setProtocolId(uint16_t protocolId) { _setRegister(tcp, 2, protocolId); } -void Modbus::setLength(uint16_t length) { +void RS485::setLength(uint16_t length) { if (length < 3 || length > 254) _setRegister(tcp, 4, 0); else _setRegister(tcp, 4, length); } -void Modbus::setUnitId(uint8_t unitId) { +void RS485::setUnitId(uint8_t unitId) { tcp[6] = unitId; } -void Modbus::setFunctionCode(uint8_t functionCode) { +void RS485::setFunctionCode(uint8_t functionCode) { pdu[0] = functionCode; } -void Modbus::setDataRegister(uint8_t index, uint16_t value) { +void RS485::setDataRegister(uint8_t index, uint16_t value) { _setRegister(data, index, value); } -void Modbus::setRtuLen(uint16_t rtuLen) { +void RS485::setRtuLen(uint16_t rtuLen) { setLength(rtuLen - 2); } -void Modbus::setTcpLen(uint16_t tcpLen) { +void RS485::setTcpLen(uint16_t tcpLen) { setLength(tcpLen - 6); } -void Modbus::setPduLen(uint16_t pduLen) { +void RS485::setPduLen(uint16_t pduLen) { setLength(pduLen + 1); } -void Modbus::setDataLen(uint16_t dataLen) { +void RS485::setDataLen(uint16_t dataLen) { setLength(dataLen + 2); } -uint16_t Modbus::getTransactionId() { +uint16_t RS485::getTransactionId() { return _getRegister(tcp, 0); } -uint16_t Modbus::getProtocolId() { +uint16_t RS485::getProtocolId() { return _getRegister(tcp, 2); } -uint16_t Modbus::getLength() { +uint16_t RS485::getLength() { uint16_t length = _getRegister(tcp, 4); if (length < 3 || length > 254) return 0; else return length; } -uint8_t Modbus::getUnitId() { +uint8_t RS485::getUnitId() { return tcp[6]; } -uint8_t Modbus::getFunctionCode() { +uint8_t RS485::getFunctionCode() { return pdu[0]; } -uint16_t Modbus::getDataRegister(uint8_t index) { +uint16_t RS485::getDataRegister(uint8_t index) { return _getRegister(data, index); } -uint16_t Modbus::getRtuLen() { +uint16_t RS485::getRtuLen() { uint16_t len = getLength(); if (len == 0) return 0; else return len + 2; } -uint16_t Modbus::getTcpLen() { +uint16_t RS485::getTcpLen() { uint16_t len = getLength(); if (len == 0) return 0; else return len + 6; } -uint16_t Modbus::getPduLen() { +uint16_t RS485::getPduLen() { uint16_t len = getLength(); if (len == 0) return 0; else return len - 1; } -uint16_t Modbus::getDataLen() { +uint16_t RS485::getDataLen() { uint16_t len = getLength(); if (len == 0) return 0; else return len - 2; @@ -120,29 +120,29 @@ uint16_t Modbus::getDataLen() { -void Modbus::updateCrc(uint8_t *buf, uint16_t len) { +void RS485::updateCrc(uint8_t *buf, uint16_t len) { uint16_t crc = _calculateCrc(buf, len); buf[len] = lowByte(crc); buf[len + 1] = highByte(crc); } -bool Modbus::crcGood(uint8_t *buf, uint16_t len) { +bool RS485::crcGood(uint8_t *buf, uint16_t len) { uint16_t aduCrc = buf[len] | (buf[len + 1] << 8); uint16_t calculatedCrc = _calculateCrc(buf, len); if (aduCrc == calculatedCrc) return true; else return false; } -void Modbus::_setRegister(uint8_t *buf, uint16_t index, uint16_t value) { +void RS485::_setRegister(uint8_t *buf, uint16_t index, uint16_t value) { buf[index] = highByte(value); buf[index + 1] = lowByte(value); } -uint16_t Modbus::_getRegister(uint8_t *buf, uint16_t index) { +uint16_t RS485::_getRegister(uint8_t *buf, uint16_t index) { return (buf[index] << 8) | buf[index + 1]; } -uint16_t Modbus::_calculateCrc(uint8_t *buf, uint16_t len) { +uint16_t RS485::_calculateCrc(uint8_t *buf, uint16_t len) { uint16_t value = 0xFFFF; for (uint16_t i = 0; i < len; i++) { value ^= (uint16_t)buf[i]; @@ -161,7 +161,7 @@ uint16_t div8RndUp(uint16_t value) { return (value + 7) >> 3; } -void Modbus::clearRxBuffer() { +void RS485::clearRxBuffer() { unsigned long startMicros = micros(); do { if (_serialD->available() > 0) { @@ -173,12 +173,12 @@ void Modbus::clearRxBuffer() { /************************************************************ - * Modbus implementation + * RS485 implementation ************************************************************/ -// Constructor for Modbus -Modbus::Modbus(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA, int waitB) { +// 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; @@ -191,15 +191,15 @@ Modbus::Modbus(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint16 // Add device to HAL device chain IODevice::addDevice(this); - // Add bus to Modbus chain. + // Add bus to RS485 chain. _nextBus = _busList; _busList = this; } -// Main loop function for Modbus. +// 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 Modbus::_loop(unsigned long currentMicros) { +void RS485::_loop(unsigned long currentMicros) { _currentMicros = currentMicros; if (_currentNode == NULL) { @@ -212,8 +212,8 @@ void Modbus::_loop(unsigned long currentMicros) { if (_currentNode == NULL) return; bool flagOK = true; -#if defined(MODBUS_STM_COMM) - ArduinoPins::fastWriteDigital(MODBUS_STM_COMM,HIGH); +#if defined(RS485_STM_COMM) + ArduinoPins::fastWriteDigital(RS485_STM_COMM,HIGH); #endif if (taskCnt > 0) { @@ -443,36 +443,36 @@ if (taskCnt > 0) { _currentNode = _currentNode->getNext(); } -#if defined(MODBUS_STM_OK) +#if defined(RS485_STM_OK) if (flagOK == true) { - ArduinoPins::fastWriteDigital(MODBUS_STM_OK,HIGH); + ArduinoPins::fastWriteDigital(RS485_STM_OK,HIGH); } else { - ArduinoPins::fastWriteDigital(MODBUS_STM_OK,LOW); + ArduinoPins::fastWriteDigital(RS485_STM_OK,LOW); } #endif -#if defined(MODBUS_STM_FAIL) +#if defined(RS485_STM_FAIL) if (flagOK == false) { - ArduinoPins::fastWriteDigital(MODBUS_STM_FAIL,HIGH); + ArduinoPins::fastWriteDigital(RS485_STM_FAIL,HIGH); } else { - ArduinoPins::fastWriteDigital(MODBUS_STM_FAIL,LOW); + ArduinoPins::fastWriteDigital(RS485_STM_FAIL,LOW); } #endif -#if defined(MODBUS_STM_COMM) - ArduinoPins::fastWriteDigital(MODBUS_STM_COMM,LOW); +#if defined(RS485_STM_COMM) + ArduinoPins::fastWriteDigital(RS485_STM_COMM,LOW); #endif } -// Link to chain of Modbus instances -Modbus *Modbus::_busList = NULL; +// Link to chain of RS485 instances +RS485 *RS485::_busList = NULL; /************************************************************ - * Modbusnode implementation + * RS485node implementation ************************************************************/ -// Constructor for Modbusnode object -Modbusnode::Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID) { +// Constructor for RS485node object +RS485node::RS485node(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID) { _firstVpin = firstVpin; _nPins = nPins; _busNo = busNo; @@ -482,8 +482,8 @@ Modbusnode::Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID) // Add this device to HAL device list IODevice::addDevice(this); _display(); - // Add Modbusnode to Modbus object. - Modbus *bus = Modbus::findBus(_busNo); + // Add RS485node to RS485 object. + RS485 *bus = RS485::findBus(_busNo); if (bus != NULL) { bus->addNode(this); return; diff --git a/IO_Modbus.h b/IO_RS485.h similarity index 69% rename from IO_Modbus.h rename to IO_RS485.h index 27f4e160..41e1d793 100644 --- a/IO_Modbus.h +++ b/IO_RS485.h @@ -19,10 +19,10 @@ */ /* - * Modbus + * RS485 * ======= - * To define a Modbus, example syntax: - * Modbus::create(bus, serial, baud[, cycletime[, pin]]); + * To define a RS485, example syntax: + * RS485::create(bus, serial, baud[, cycletime[, pin]]); * * bus = 0-255 * serial = serial port to be used (e.g. Serial3) @@ -32,7 +32,7 @@ * * Each bus must use a different serial port. * - * ModbusNode + * RS485Node * ======== * To define a CMRI node and associate it with a CMRI bus, * CMRInode::create(firstVPIN, numVPINs, bus, nodeID, type [, inputs, outputs]); @@ -41,56 +41,29 @@ * numVPINs = number of vpins (e.g. 72 for an SMINI node) * bus = 0-255 * nodeID = 0-127 - * numDiscreteInputs = number of discrete inputs - * numCoils = number of coils - * - * Reference: "LCS-9.10.1 - * Layout Control Specification: CMRInet Protocol - * Version 1.1 December 2014." */ -#ifndef IO_MODBUS_H -#define IO_MODBUS_H +#ifndef IO_RS485_H +#define IO_RS485_H #include "IODevice.h" uint16_t div8RndUp(uint16_t value); /********************************************************************** - * Modbusnode class + * RS485node class * - * This encapsulates the state associated with a single Modbus node, + * This encapsulates the state associated with a single RS485 node, * which includes the nodeID, number of discrete inputs and coils, and * the states of the discrete inputs and coils. **********************************************************************/ -class Modbusnode : public IODevice { +class RS485node : public IODevice { private: uint8_t _busNo; uint8_t _nodeID; char _type; - Modbusnode *_next = NULL; + RS485node *_next = NULL; bool _initialised = false; - static const uint8_t _numCoils=100; - static const uint8_t _numDiscreteInputs=100; - static const uint8_t _numHoldingRegisters=100; - static const uint8_t _numInputRegisters=100; - uint8_t _numBO=0; - uint8_t _numBI=0; - uint8_t _numAO=0; - uint8_t _numAI=0; - int dataBO[16]; - int dataBI[16]; - int dataAO[84]; - int dataAI[84]; - int capePinsBI[16]; - int capePinsBO[16]; - int capePinsPU[16]; - int capePinsAO[16]; - int capePinsAI[16]; - int configBPinsO[16]; - int configBPinsI[16]; - int configBPinsPU[16]; - int configAPinsO[16]; - int configAPinsI[16]; + // EX-IOExpander protocol flags enum { EXIOINIT = 0xE0, // Flag to initialise setup procedure @@ -106,43 +79,6 @@ class Modbusnode : public IODevice { EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM) EXIOERR = 0xEF, // Flag we've received an error }; - void resetInit() { - for (int i = 0; i < 16; i++) { - capePinsBI[i] = 0; - capePinsBO[i] = 0; - capePinsPU[i] = 0; - capePinsAO[i] = 0; - capePinsAI[i] = 0; - configBPinsO[i] = 0; - configBPinsI[i] = 0; - configBPinsPU[i] = 0; - configAPinsO[i] = 0; - configAPinsI[i] = 0; - } - } - - - - void spitError(int pin) { - bool isBI = false; - bool isBO = false; - bool isPU = false; - bool isAI = false; - bool isAO = false; - int configPinNum = pin / 16; - int configPinBit = pin % 16; - if (bitRead(configBPinsI[configPinNum],configPinBit) == true) isBI = true; - if (bitRead(configBPinsO[configPinNum],configPinBit) == true) isBO = true; - if (bitRead(configBPinsPU[configPinNum],configPinBit) == true) isPU = true; - if (bitRead(configAPinsI[configPinNum],configPinBit) == true) isAI = true; - if (bitRead(configAPinsO[configPinNum],configPinBit) == true) isAO = true; - if (isBI && isPU) DIAG(F("IO_Modbus config eror: Bool Input with pull-up, pin: %d"),pin); - if (isBI && !isPU) DIAG(F("IO_Modbus config eror: Bool Input without pull-up, pin: %d"),pin); - if (isBO) DIAG(F("IO_Modbus config eror: Bool Output, pin: %d"),pin); - if (isAI) DIAG(F("IO_Modbus config eror: Analog Input, pin: %d"),pin); - if (isAO) DIAG(F("IO_Modbus config eror: Analog Output, pin: %d"),pin); - - } public: enum ProfileType : int { @@ -172,35 +108,18 @@ class Modbusnode : public IODevice { uint8_t* _analoguePinMap = NULL; static void create(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID) { - if (checkNoOverlap(firstVpin, nPins)) new Modbusnode(firstVpin, nPins, busNo, nodeID); + if (checkNoOverlap(firstVpin, nPins)) new RS485node(firstVpin, nPins, busNo, nodeID); } - Modbusnode(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID); - int *coils[_numCoils]; - int *discreteInputs[_numDiscreteInputs]; - uint16_t *holdingRegisters[_numHoldingRegisters]; - uint16_t *inputRegisters[_numInputRegisters]; + RS485node(VPIN firstVpin, int nPins, uint8_t busNo, uint8_t nodeID); uint8_t getNodeID() { return _nodeID; } - uint8_t getNumCoils() { - return _numCoils; - } - uint8_t getNumDiscreteInputs() { - return _numDiscreteInputs; - } - uint8_t getNumHoldingRegisters() { - return _numHoldingRegisters; - } - uint8_t getNumInputRegisters() { - return _numInputRegisters; - } - - Modbusnode *getNext() { + RS485node *getNext() { return _next; } - void setNext(Modbusnode *node) { + void setNext(RS485node *node) { _next = node; } bool isInitialised() { @@ -209,61 +128,12 @@ class Modbusnode : public IODevice { void setInitialised() { _initialised = true; } - - bool addPinBI(VPIN vpin, bool inputPullup) { - int configPinNum = vpin / 16; - int configPinBit = vpin % 16; - bitSet(configBPinsI[configPinNum],configPinBit); // input - bitWrite(configBPinsPU[configPinNum],configPinBit,inputPullup); - if (_numBI + _numBO + _numAI + _numAO > _nPins) { - DIAG(F("IO_Modbus config error: Too many I/O pins vs VPINs: %d"),_numBI + _numBO + _numAI + _numAO); - return true; - } - _numBI++; - return false; - } - - bool addPinBO(VPIN vpin) { - int configPinNum = vpin / 16; - int configPinBit = vpin % 16; - bitSet(configBPinsO[configPinNum],configPinBit); // input - if (_numBI + _numBO + _numAI + _numAO > _nPins) { - DIAG(F("IO_Modbus config error: Too many I/O pins vs VPINs: %d"),_numBI + _numBO + _numAI + _numAO); - return true; - } - _numBO++; - return false; - } - - bool addPinAI(VPIN vpin) { - int configPinNum = vpin / 6; - int configPinBit = vpin % 16; - bitSet(configAPinsI[configPinNum],configPinBit); // input - if (_numBI + _numBO + _numAI + _numAO > _nPins) { - DIAG(F("IO_Modbus config error: Too many I/O pins vs VPINs: %d"),_numBI + _numBO + _numAI + _numAO); - return true; - } - _numAI++; - return false; - } - - bool addPinAO(VPIN vpin) { - int configPinNum = vpin / 6; - int configPinBit = vpin % 16; - bitSet(configAPinsO[configPinNum],configPinBit); // input - if (_numBI + _numBO + _numAI + _numAO > _nPins) { - DIAG(F("IO_Modbus config error: Too many I/O pins vs VPINs: %d"),_numBI + _numBO + _numAI + _numAO); - return true; - } - _numBI++; - return false; - } bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override { if (paramCount != 1) return false; int pin = vpin - _firstVpin; int pin = vpin - _firstVpin; - Modbus* mb = Modbus::findBus(0); + RS485* mb = RS485::findBus(0); int* param[] = {(int*)pin, (int*)configType, (int*)paramCount, (int*)params[0]}; mb->addTask(_nodeID, 3, 4, param); @@ -271,7 +141,7 @@ class Modbusnode : public IODevice { int _configureAnalogIn(VPIN vpin) override { int pin = vpin - _firstVpin; - Modbus* mb = Modbus::findBus(0); + RS485* mb = RS485::findBus(0); int* params[] = {(int*)pin}; mb->addTask(_nodeID, 3, 1, params); @@ -279,7 +149,7 @@ class Modbusnode : public IODevice { } void _begin() override { - Modbus* mb = Modbus::findBus(0); + RS485* mb = RS485::findBus(0); if (mb->_txPin != VPIN_NONE) { pinMode(mb->_txPin, OUTPUT); ArduinoPins::fastWriteDigital(mb->_txPin, LOW); @@ -429,7 +299,7 @@ class Modbusnode : public IODevice { void _write(VPIN vpin, int value) override { if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; - Modbus* mb = Modbus::findBus(0); + RS485* mb = RS485::findBus(0); int* params[] = {(int*)pin, (int*)value}; mb->addTask(_nodeID, 3, 2, params); } @@ -453,7 +323,7 @@ class Modbusnode : public IODevice { if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; - Modbus* mb = Modbus::findBus(0); + RS485* mb = RS485::findBus(0); int* params[] = {(int*)pin, (int*)value, (int*)profile, (int*)duration}; mb->addTask(_nodeID, 3, 4, params); } @@ -461,62 +331,23 @@ class Modbusnode : public IODevice { uint8_t getBusNumber() { return _busNo; } - uint8_t getNumBinaryInputsVPINsMin() { - if (_numDiscreteInputs > 0) return _firstVpin; - else return 0; - } - uint8_t getNumBinaryInputsVPINsMax() { - if (_numDiscreteInputs > 0) return _firstVpin+_numDiscreteInputs-1; - else return 0; - } - - uint8_t getNumBinaryOutputsVPINsMin() { - if (_numCoils > 0) return _firstVpin+_numDiscreteInputs; - else return 0; - } - uint8_t getNumBinaryOutputsVPINsMax() { - if (_numCoils > 0) return _firstVpin+_numDiscreteInputs+_numCoils-1; - else return 0; - } - - uint8_t getNumAnalogInputsVPINsMin() { - if (_numInputRegisters > 0) return _firstVpin+_numDiscreteInputs+_numCoils; - else return 0; - } - uint8_t getNumAnalogInputsVPINsMax() { - if (_numInputRegisters > 0) return _firstVpin+_numDiscreteInputs+_numCoils+_numInputRegisters-1; - else return 0; - } - - uint8_t getNumAnalogOutputsVPINsMin() { - if (_numHoldingRegisters > 0) return _firstVpin+_numDiscreteInputs+_numCoils+_numInputRegisters; - else return 0; - } - uint8_t getNumAnalogOutputsVPINsMax() { - if (_numHoldingRegisters > 0) return _firstVpin+_numDiscreteInputs+_numCoils+_numInputRegisters+_numHoldingRegisters-1; - else return 0; - } + void _display() override { - DIAG(F("Modbusnode configured on bus:%d nodeID:%d VPINs:%u-%u (B In) %u-%u (B Out) %u-%u (A In) %u-%u (A Out)"), - _busNo, _nodeID, getNumBinaryInputsVPINsMin(), getNumBinaryInputsVPINsMax(), - getNumBinaryOutputsVPINsMin(), getNumBinaryOutputsVPINsMax(), - getNumAnalogInputsVPINsMin(), getNumAnalogInputsVPINsMax(), - getNumAnalogOutputsVPINsMin(), getNumAnalogOutputsVPINsMax()); + 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("")); } - }; /********************************************************************** - * Modbus class + * RS485 class * * This encapsulates the properties state of the bus and the - * transmission and reception of data across that bus. Each Modbus - * object owns a set of Modbusnode objects which represent the nodes + * transmission and reception of data across that bus. Each RS485 + * object owns a set of RS485node objects which represent the nodes * attached to that bus. **********************************************************************/ -class Modbus : public IODevice { +class RS485 : public IODevice { private: // Here we define the device-specific variables. uint8_t _busNo; @@ -526,12 +357,12 @@ class Modbus : public IODevice { void _setRegister(uint8_t *buf, uint16_t index, uint16_t value); unsigned long _baud; - Modbusnode *_nodeListStart = NULL, *_nodeListEnd = NULL; - Modbusnode *_currentNode = NULL; + 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. - Modbus *_nextBus = NULL; // Pointer to next bus instance in list. + RS485 *_nextBus = NULL; // Pointer to next bus instance in list. void setTimeout(unsigned long timeout); unsigned long _cycleStartTime = 0; unsigned long _timeoutStart = 0; @@ -542,7 +373,7 @@ class Modbus : public IODevice { unsigned long _byteTransmitTime; // time in us for transmission of one byte int _operationCount = 0; - static Modbus *_busList; // linked list of defined bus instances + static RS485 *_busList; // linked list of defined bus instances bool waitReceive = false; int _waitCounter = 0; int _waitCounterB = 0; @@ -673,7 +504,7 @@ class Modbus : public IODevice { 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 Modbus(busNo, serial, baud, cycleTimeMS, txPin, waitA, waitB); + new RS485(busNo, serial, baud, cycleTimeMS, txPin, waitA, waitB); } HardwareSerial *_serialD; // Device-specific initialisation @@ -689,17 +520,17 @@ class Modbus : public IODevice { _frameTimeout = (bitsPerChar * 1000000) / _baud + 1750; } clearRxBuffer(); - #if defined(MODBUS_STM_OK) - pinMode(MODBUS_STM_OK, OUTPUT); - ArduinoPins::fastWriteDigital(MODBUS_STM_OK,LOW); + #if defined(RS485_STM_OK) + pinMode(RS485_STM_OK, OUTPUT); + ArduinoPins::fastWriteDigital(RS485_STM_OK,LOW); #endif - #if defined(MODBUS_STM_FAIL) - pinMode(MODBUS_STM_FAIL, OUTPUT); - ArduinoPins::fastWriteDigital(MODBUS_STM_FAIL,LOW); + #if defined(RS485_STM_FAIL) + pinMode(RS485_STM_FAIL, OUTPUT); + ArduinoPins::fastWriteDigital(RS485_STM_FAIL,LOW); #endif - #if defined(MODBUS_STM_COMM) - pinMode(MODBUS_STM_COMM, OUTPUT); - ArduinoPins::fastWriteDigital(MODBUS_STM_COMM,LOW); + #if defined(RS485_STM_COMM) + pinMode(RS485_STM_COMM, OUTPUT); + ArduinoPins::fastWriteDigital(RS485_STM_COMM,LOW); #endif #if defined(DIAG_IO) @@ -716,13 +547,13 @@ class Modbus : public IODevice { // Display information about the device void _display() override { - DIAG(F("Modbus Configured on Vpins:%d-%d %S"), _firstVpin, _firstVpin+_nPins-1, + DIAG(F("RS485 Configured on Vpins:%d-%d %S"), _firstVpin, _firstVpin+_nPins-1, _deviceState == DEVSTATE_FAILED ? F("OFFLINE") : F("OK")); } - // Locate Modbusnode object with specified nodeID. - Modbusnode *findNode(uint8_t nodeID) { - for (Modbusnode *node = _nodeListStart; node != NULL; node = node->getNext()) { + // Locate RS485node object with specified nodeID. + RS485node *findNode(uint8_t nodeID) { + for (RS485node *node = _nodeListStart; node != NULL; node = node->getNext()) { if (node->getNodeID() == nodeID) return node; } @@ -730,19 +561,19 @@ class Modbus : public IODevice { } - // Add new Modbusnode to the list of nodes for this bus. - void addNode(Modbusnode *newNode) { + // Add new RS485node to the list of nodes for this bus. + void addNode(RS485node *newNode) { if (!_nodeListStart) _nodeListStart = newNode; if (!_nodeListEnd) _nodeListEnd = newNode; else _nodeListEnd->setNext(newNode); - //DIAG(F("Modbus: 260h nodeID:%d _nodeListStart:%d _nodeListEnd:%d"), newNode, _nodeListStart, _nodeListEnd); + //DIAG(F("RS485: 260h nodeID:%d _nodeListStart:%d _nodeListEnd:%d"), newNode, _nodeListStart, _nodeListEnd); } protected: - Modbus(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA, int waitB); + RS485(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA, int waitB); public: @@ -750,8 +581,8 @@ class Modbus : public IODevice { return _busNo; } - static Modbus *findBus(uint8_t busNo) { - for (Modbus *bus=_busList; bus!=NULL; bus=bus->_nextBus) { + static RS485 *findBus(uint8_t busNo) { + for (RS485 *bus=_busList; bus!=NULL; bus=bus->_nextBus) { if (bus->_busNo == busNo) return bus; } return NULL; @@ -759,4 +590,4 @@ class Modbus : public IODevice { }; -#endif // IO_MODBUS_H +#endif // IO_RS485_H \ No newline at end of file From 53d23770f6618cda0d4a89f0cd143da2e67a6eed Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Fri, 13 Dec 2024 04:04:13 -0500 Subject: [PATCH 43/73] tidy up and debug --- IO_RS485.cpp | 365 +++++++++++++++++++++------------------------------ IO_RS485.h | 213 +++++++++++++----------------- 2 files changed, 235 insertions(+), 343 deletions(-) diff --git a/IO_RS485.cpp b/IO_RS485.cpp index ade5013e..f3cbfc94 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); -} - -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; -} +/************************************************************ + * RS485 implementation + ************************************************************/ -uint16_t RS485::getPduLen() { - uint16_t len = getLength(); - if (len == 0) return 0; - else return len - 1; -} +// 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; -uint16_t RS485::getDataLen() { - uint16_t len = getLength(); - if (len == 0) return 0; - else return len - 2; + _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; } - - +/* -= 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 41e1d793..e5629594 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 @@ class RS485node : public IODevice { 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 @@ class RS485node : public IODevice { 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 @@ class RS485node : public IODevice { 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 @@ class RS485node : public IODevice { _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 @@ class RS485node : public IODevice { } } } 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 @@ class RS485node : public IODevice { 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 @@ class RS485node : public IODevice { 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 @@ class RS485node : public IODevice { } 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 @@ class RS485 : public IODevice { 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 @@ class RS485 : public IODevice { 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 @@ class RS485 : public IODevice { 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 @@ class RS485 : public IODevice { 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 @@ class RS485 : public IODevice { _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 @@ class RS485 : public IODevice { _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 @@ class RS485 : public IODevice { } 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: From cf833b3bb011b7acb45bfa3967577b158c3f9342 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Fri, 13 Dec 2024 05:16:36 -0500 Subject: [PATCH 44/73] cleaning and debugging --- IO_RS485.cpp | 12 +++++++++--- IO_RS485.h | 12 ++++++++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/IO_RS485.cpp b/IO_RS485.cpp index f3cbfc94..f8bc4f97 100644 --- a/IO_RS485.cpp +++ b/IO_RS485.cpp @@ -154,6 +154,7 @@ if (taskCnt > 0) { } } while (micros() - startMicros <= 500 && len < 256); if (crcGood(responseBuffer,sizeof(responseBuffer)-2)) { + if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); if (responseBuffer[0] == EXIORDY) { } else { DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), (int)taskData[3]); @@ -196,6 +197,7 @@ if (taskCnt > 0) { } while (micros() - startMicros <= 500 && len < 256); if (crcGood(responseBuffer,sizeof(responseBuffer)-2)) { + if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); if (responseBuffer[0] != EXIORDY) { DIAG(F("EX-IOExpander485: Vpin %u on node %d cannot be used as an analogue input pin"), (int) taskData[3], (int) taskData[0]); } @@ -234,6 +236,7 @@ if (taskCnt > 0) { } } while (micros() - startMicros <= 500 && len < 256); if (crcGood(responseBuffer,sizeof(responseBuffer)-2)) { + if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); if (responseBuffer[0] != EXIORDY) { DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital output pin"), (int)taskData[3]); } @@ -280,10 +283,11 @@ if (taskCnt > 0) { } } while (micros() - startMicros <= 500 && len < 256); if (!crcGood(responseBuffer,sizeof(responseBuffer)-2)) { - DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) taskData[0]); - flagOK = false; + DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) taskData[0]); + flagOK = false; _deviceState = DEVSTATE_FAILED; } else { + if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); if (responseBuffer[0] != EXIORDY) { DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a servo/PWM pin"), (int) taskData[3]); } @@ -324,6 +328,7 @@ if (taskCnt > 0) { DIAG(F("EX-IOExpander485 CRC error on node %d"), _currentNode->getNodeID()); flagOK = false; } + if (!testAndStripMasterFlag(_currentNode->_digitalInputStates)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); if (!waitReceive) _refreshOperation++; _lastDigitalRead = currentMicros; _readState = RDS_DIGITAL; @@ -360,6 +365,7 @@ if (taskCnt > 0) { DIAG(F("EX-IOExpander485 CRC error on node %d"), _currentNode->getNodeID()); flagOK = false; } + if (!testAndStripMasterFlag(_currentNode->_digitalInputStates)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); if (!waitReceive) _refreshOperation = 0; _lastAnalogueRead = currentMicros; _readState = RDS_ANALOGUE; @@ -406,7 +412,7 @@ RS485node::RS485node(VPIN firstVpin, int nPins, uint8_t nodeID) { _nPins = nPins; _busNo = 0; _nodeID = nodeID; - if (_nodeID > 255) _nodeID = 255; + if (_nodeID > 254) _nodeID = 254; // Add this device to HAL device list IODevice::addDevice(this); diff --git a/IO_RS485.h b/IO_RS485.h index e5629594..b21b69ed 100644 --- a/IO_RS485.h +++ b/IO_RS485.h @@ -37,7 +37,7 @@ * * firstVPIN = first vpin in block allocated to this device * numVPINs = number of vpins - * nodeID = 0-255 + * nodeID = 0-254 */ #ifndef IO_RS485_H @@ -170,7 +170,8 @@ class RS485node : public IODevice { len++; } } while (micros() - startMicros <= 500 && len < 256); - if (receiveBuffer[0] == EXIOPINS && bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { + if (receiveBuffer[1] == EXIOPINS && bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { + if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_nodeID); _numDigitalPins = receiveBuffer[1]; _numAnaloguePins = receiveBuffer[2]; @@ -241,6 +242,7 @@ class RS485node : public IODevice { } } while (micros() - startMicros <= 500 && len < 256); if (bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { + if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_nodeID); for (int i = 0; i < _numAnaloguePins; i++) { _analoguePinMap[i] = receiveBuffer[i]; } @@ -267,6 +269,7 @@ class RS485node : public IODevice { } } while (micros() - startMicros <= 500 && len < 256); if (bus->crcGood(versionBuffer,sizeof(versionBuffer)-2)) { + if (!bus->testAndStripMasterFlag(versionBuffer)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_nodeID); _majorVer = versionBuffer[0]; _minorVer = versionBuffer[1]; _patchVer = versionBuffer[2]; @@ -420,6 +423,11 @@ class RS485 : public IODevice { int8_t _txPin; int taskCnt = 0; HardwareSerial *_serialD; + bool testAndStripMasterFlag(uint8_t *buf) { + if (buf[0] != 0xFF) return false; // why did we not get a master flag? bad node? + for (int i = 0; i < sizeof(buf)-1; i++) buf[i] = buf[i+1]; // shift array to begining + return true; + } void addTask(int nodeID, int taskNum, int paramCnt, int *param[]) { taskCnt++; tasks[taskCnt][0] = nodeID; From 428796fa9c09c495d2340fdf2e698755aa7be860 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Fri, 13 Dec 2024 05:22:28 -0500 Subject: [PATCH 45/73] miss-spelled DIAG text --- IO_RS485.cpp | 12 ++++++------ IO_RS485.h | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/IO_RS485.cpp b/IO_RS485.cpp index f8bc4f97..a6be6a9a 100644 --- a/IO_RS485.cpp +++ b/IO_RS485.cpp @@ -154,7 +154,7 @@ if (taskCnt > 0) { } } while (micros() - startMicros <= 500 && len < 256); if (crcGood(responseBuffer,sizeof(responseBuffer)-2)) { - if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); + if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); if (responseBuffer[0] == EXIORDY) { } else { DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), (int)taskData[3]); @@ -197,7 +197,7 @@ if (taskCnt > 0) { } while (micros() - startMicros <= 500 && len < 256); if (crcGood(responseBuffer,sizeof(responseBuffer)-2)) { - if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); + if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); if (responseBuffer[0] != EXIORDY) { DIAG(F("EX-IOExpander485: Vpin %u on node %d cannot be used as an analogue input pin"), (int) taskData[3], (int) taskData[0]); } @@ -236,7 +236,7 @@ if (taskCnt > 0) { } } while (micros() - startMicros <= 500 && len < 256); if (crcGood(responseBuffer,sizeof(responseBuffer)-2)) { - if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); + if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); if (responseBuffer[0] != EXIORDY) { DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital output pin"), (int)taskData[3]); } @@ -287,7 +287,7 @@ if (taskCnt > 0) { flagOK = false; _deviceState = DEVSTATE_FAILED; } else { - if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); + if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); if (responseBuffer[0] != EXIORDY) { DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a servo/PWM pin"), (int) taskData[3]); } @@ -328,7 +328,7 @@ if (taskCnt > 0) { DIAG(F("EX-IOExpander485 CRC error on node %d"), _currentNode->getNodeID()); flagOK = false; } - if (!testAndStripMasterFlag(_currentNode->_digitalInputStates)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); + if (!testAndStripMasterFlag(_currentNode->_digitalInputStates)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); if (!waitReceive) _refreshOperation++; _lastDigitalRead = currentMicros; _readState = RDS_DIGITAL; @@ -365,7 +365,7 @@ if (taskCnt > 0) { DIAG(F("EX-IOExpander485 CRC error on node %d"), _currentNode->getNodeID()); flagOK = false; } - if (!testAndStripMasterFlag(_currentNode->_digitalInputStates)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); + if (!testAndStripMasterFlag(_currentNode->_digitalInputStates)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); if (!waitReceive) _refreshOperation = 0; _lastAnalogueRead = currentMicros; _readState = RDS_ANALOGUE; diff --git a/IO_RS485.h b/IO_RS485.h index b21b69ed..bf64afdf 100644 --- a/IO_RS485.h +++ b/IO_RS485.h @@ -171,7 +171,7 @@ class RS485node : public IODevice { } } while (micros() - startMicros <= 500 && len < 256); if (receiveBuffer[1] == EXIOPINS && bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { - if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_nodeID); + if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_nodeID); _numDigitalPins = receiveBuffer[1]; _numAnaloguePins = receiveBuffer[2]; @@ -242,7 +242,7 @@ class RS485node : public IODevice { } } while (micros() - startMicros <= 500 && len < 256); if (bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { - if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_nodeID); + if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_nodeID); for (int i = 0; i < _numAnaloguePins; i++) { _analoguePinMap[i] = receiveBuffer[i]; } @@ -269,7 +269,7 @@ class RS485node : public IODevice { } } while (micros() - startMicros <= 500 && len < 256); if (bus->crcGood(versionBuffer,sizeof(versionBuffer)-2)) { - if (!bus->testAndStripMasterFlag(versionBuffer)) DIAG(F("Forgen RS485 Device! no master flag from node %d"),_nodeID); + if (!bus->testAndStripMasterFlag(versionBuffer)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_nodeID); _majorVer = versionBuffer[0]; _minorVer = versionBuffer[1]; _patchVer = versionBuffer[2]; From 577511f92f1466aedc77adebb71aea3bb0aa6ed7 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Fri, 13 Dec 2024 09:04:12 -0500 Subject: [PATCH 46/73] rename branch, remove possible blockers --- IO_RS485.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IO_RS485.cpp b/IO_RS485.cpp index a6be6a9a..ccb3a10f 100644 --- a/IO_RS485.cpp +++ b/IO_RS485.cpp @@ -285,7 +285,7 @@ if (taskCnt > 0) { if (!crcGood(responseBuffer,sizeof(responseBuffer)-2)) { DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) taskData[0]); flagOK = false; - _deviceState = DEVSTATE_FAILED; + //_deviceState = DEVSTATE_FAILED; } else { if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); if (responseBuffer[0] != EXIORDY) { @@ -372,7 +372,7 @@ if (taskCnt > 0) { } break; } - if(flagOK == true) _currentNode = _currentNode->getNext(); + if(flagOK && !waitReceive) _currentNode = _currentNode->getNext(); } #if defined(RS485_STM_OK) From 3b2ea6f3371fc9b7cc76c16222505fff690c755d Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Fri, 13 Dec 2024 16:46:17 -0500 Subject: [PATCH 47/73] does not compile, saving current --- IO_RSproto.h | 640 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 640 insertions(+) create mode 100644 IO_RSproto.h diff --git a/IO_RSproto.h b/IO_RSproto.h new file mode 100644 index 00000000..950f5b26 --- /dev/null +++ b/IO_RSproto.h @@ -0,0 +1,640 @@ +/* + * © 2024, Travis Farmer. All rights reserved. + * © 2024, Chris Bulliner. All rights reserved. https://github.com/CMB27 + * + * This file is part of DCC++EX API + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + +/* + * RSproto + * ======= + * To define a RSproto, example syntax: + * RSproto::create(serial, baud[, cycletime[, pin]]); + * + * 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 RSproto module's DE and !RE terminals for half-duplex operation (default VPIN_NONE) + * + * + * RSprotoNode + * ======== + * To define a RSproto node and associate it with a RSproto bus, + * RSprotonode::create(firstVPIN, numVPINs, nodeID); + * + * firstVPIN = first vpin in block allocated to this device + * numVPINs = number of vpins + * nodeID = 0-254 + */ + +#ifndef IO_RS485_H +#define IO_RS485_H + +#include "IODevice.h" + +/********************************************************************** + * RSprotonode class + * + * This encapsulates the state associated with a single RSproto node, + * which includes the nodeID, number of discrete inputs and coils, and + * the states of the discrete inputs and coils. + **********************************************************************/ +class RSprotonode : public IODevice { +private: + uint8_t _busNo; + uint8_t _nodeID; + char _type; + RSprotonode *_next = NULL; + bool _initialised = false; + RSproto* bus = NULL; + // EX-IOExpander protocol flags + enum { + EXIOINIT = 0xE0, // Flag to initialise setup procedure + EXIORDY = 0xE1, // Flag we have completed setup procedure, also for EX-IO to ACK setup + EXIODPUP = 0xE2, // Flag we're sending digital pin pullup configuration + EXIOVER = 0xE3, // Flag to get version + EXIORDAN = 0xE4, // Flag to read an analogue input + EXIOWRD = 0xE5, // Flag for digital write + EXIORDD = 0xE6, // Flag to read digital input + EXIOENAN = 0xE7, // Flag to enable an analogue pin + EXIOINITA = 0xE8, // Flag we're receiving analogue pin mappings + EXIOPINS = 0xE9, // Flag we're receiving pin counts for buffers + EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM) + EXIOERR = 0xEF, // Flag we've received an error + }; + +public: + enum ProfileType : int { + Instant = 0, // Moves immediately between positions (if duration not specified) + UseDuration = 0, // Use specified duration + Fast = 1, // Takes around 500ms end-to-end + Medium = 2, // 1 second end-to-end + Slow = 3, // 2 seconds end-to-end + Bounce = 4, // For semaphores/turnouts with a bit of bounce!! + NoPowerOff = 0x80, // Flag to be ORed in to suppress power off after move. + }; + + uint8_t _numDigitalPins = 0; + uint8_t _numAnaloguePins = 0; + + uint8_t _majorVer = 0; + uint8_t _minorVer = 0; + uint8_t _patchVer = 0; + + uint8_t* _digitalInputStates = NULL; + uint8_t* _analogueInputStates = NULL; + uint8_t* _analogueInputBuffer = NULL; // buffer for I2C input transfers + 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 nodeID) { + if (checkNoOverlap(firstVpin, nPins)) new RSprotonode(firstVpin, nPins, nodeID); + } + RSprotonode(VPIN firstVpin, int nPins, uint8_t nodeID); + + uint8_t getNodeID() { + return _nodeID; + } + + RSprotonode *getNext() { + return _next; + } + void setNext(RSprotonode *node) { + _next = node; + } + bool isInitialised() { + return _initialised; + } + void setInitialised() { + _initialised = true; + } + + bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override { + if (paramCount != 1) return false; + int pin = vpin - _firstVpin; + + uint16_t pullup = params[0]; + uint8_t outBuffer[6] = {EXIODPUP, _nodeID, pin, pullup}; + uint8_t responseBuffer[3]; + bus->_busy = true; + bus->updateCrc(outBuffer,4); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); + bus->_serialD->write(outBuffer, 6); + bus->_serialD->flush(); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); + unsigned long startMillis = millis(); + while (!bus->_serialD->available()) { + if (millis() - startMillis > 500) return false; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (bus->_serialD->available()) { + startMicros = micros(); + responseBuffer[len] = bus->_serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + bus->_busy = false; + if (bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)) { + if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); + if (responseBuffer[0] == EXIORDY) { + } else { + DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); + } + } else { + DIAG(F("EX-IOExpander485 node %d CRC Error"), _nodeID); + } + + } + + int _configureAnalogIn(VPIN vpin) override { + int pin = vpin - _firstVpin; + //RSproto *mainrs = RSproto::findBus(_busNo); + uint8_t commandBuffer[5] = {EXIOENAN, (uint8_t) _nodeID, (uint8_t) pin}; + uint8_t responseBuffer[3]; + bus->_busy = true; + bus->updateCrc(commandBuffer,3); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); + bus->_serialD->write(commandBuffer, 5); + bus->_serialD->flush(); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); + unsigned long startMillis = millis(); + while (!bus->_serialD->available()) { + if (millis() - startMillis > 500) return 0; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (bus->_serialD->available()) { + startMicros = micros(); + responseBuffer[len] = bus->_serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + bus->_busy = false; + if (bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)) { + if (!bus->testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); + if (responseBuffer[0] != EXIORDY) { + DIAG(F("EX-IOExpander485: Vpin %u on node %d cannot be used as an analogue input pin"), (int) pin, (int) _nodeID); + } + } else { + DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) _nodeID); + } + return false; + } + + void _begin() override { + 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)}; + 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 (!bus->_serialD->available()) { + if (millis() - startMillis >= 500) return; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (bus->_serialD->available()) { + startMicros = micros(); + receiveBuffer[len] = bus->_serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + if (receiveBuffer[1] == EXIOPINS && bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { + if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); + _numDigitalPins = receiveBuffer[1]; + _numAnaloguePins = receiveBuffer[2]; + + // See if we already have suitable buffers assigned + if (_numDigitalPins>0) { + size_t digitalBytesNeeded = (_numDigitalPins + 7) / 8; + if (_digitalPinBytes < digitalBytesNeeded) { + // Not enough space, free any existing buffer and allocate a new one + if (_digitalPinBytes > 0) free(_digitalInputStates); + if ((_digitalInputStates = (byte*) calloc(digitalBytesNeeded, 1)) != NULL) { + _digitalPinBytes = digitalBytesNeeded; + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), _nodeID, digitalBytesNeeded); + _deviceState = DEVSTATE_FAILED; + _digitalPinBytes = 0; + return; + } + } + } + + if (_numAnaloguePins>0) { + size_t analogueBytesNeeded = _numAnaloguePins * 2; + if (_analoguePinBytes < analogueBytesNeeded) { + // Free any existing buffers and allocate new ones. + if (_analoguePinBytes > 0) { + free(_analogueInputBuffer); + free(_analogueInputStates); + free(_analoguePinMap); + } + _analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1); + _analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1); + _analoguePinMap = (uint8_t*) calloc(_numAnaloguePins, 1); + if (_analogueInputStates != NULL && + _analogueInputBuffer != NULL && + _analoguePinMap != NULL) { + _analoguePinBytes = analogueBytesNeeded; + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), _nodeID); + _deviceState = DEVSTATE_FAILED; + _analoguePinBytes = 0; + return; + } + } + } + } else { + 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; + 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 (!bus->_serialD->available()) { + if (millis() - startMillis >= 500) return; + } + len = 0; + startMicros = micros(); + do { + if (bus->_serialD->available()) { + startMicros = micros(); + receiveBuffer[len] = bus->_serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + if (bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { + if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); + for (int i = 0; i < _numAnaloguePins; i++) { + _analoguePinMap[i] = receiveBuffer[i]; + } + } + uint8_t versionBuffer[5]; + commandBuffer[0] = EXIOVER; + 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 (!bus->_serialD->available()) { + if (millis() - startMillis >= 500) return; + } + len = 0; + startMicros = micros(); + do { + if (bus->_serialD->available()) { + startMicros = micros(); + versionBuffer[len] = bus->_serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + if (bus->crcGood(versionBuffer,sizeof(versionBuffer)-2)) { + if (!bus->testAndStripMasterFlag(versionBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); + _majorVer = versionBuffer[0]; + _minorVer = versionBuffer[1]; + _patchVer = versionBuffer[2]; + DIAG(F("EX-IOExpander485 device found, node:%d, Version v%d.%d.%d"), _nodeID, _majorVer, _minorVer, _patchVer); + } +#ifdef DIAG_IO + _display(); +#endif + _initialised = false; + } + + + int _read(VPIN vpin) override { + if (_deviceState == DEVSTATE_FAILED) return 0; + int pin = vpin - _firstVpin; + uint8_t pinByte = pin / 8; + bool value = bitRead(_digitalInputStates[pinByte], pin - pinByte * 8); + return value; + } + + + void _write(VPIN vpin, int value) override { + if (_deviceState == DEVSTATE_FAILED) return; + int pin = vpin - _firstVpin; + uint8_t digitalOutBuffer[6]; + uint8_t responseBuffer[3]; + digitalOutBuffer[0] = EXIOWRD; + digitalOutBuffer[1] = (uint8_t) _nodeID; + digitalOutBuffer[2] = (uint8_t) pin; + digitalOutBuffer[3] = (uint8_t) value; + bus->_busy = true; + bus->updateCrc(digitalOutBuffer,4); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); + bus->_serialD->write(digitalOutBuffer, 6); + bus->_serialD->flush(); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); + unsigned long startMillis = millis(); + while (!bus->_serialD->available()) { + if (millis() - startMillis >= 500) return; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (bus->_serialD->available()) { + startMicros = micros(); + responseBuffer[len] = bus->_serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + bus->_busy = false; + if (bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)) { + if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); + if (responseBuffer[0] != EXIORDY) { + DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital output pin"), pin); + } + } else { + DIAG(F("EX-IOExpander485 node %d CRC Error"), _nodeID); + } + } + + bool testAndStripMasterFlag(uint8_t *buf) { + if (buf[0] != 0xFF) return false; // why did we not get a master flag? bad node? + for (int i = 0; i < sizeof(buf)-1; i++) buf[i] = buf[i+1]; // shift array to begining + return true; + } + + int _readAnalogue(VPIN vpin) override { + if (_deviceState == DEVSTATE_FAILED) return 0; + int pin = vpin - _firstVpin; + for (uint8_t aPin = 0; aPin < _numAnaloguePins; aPin++) { + if (_analoguePinMap[aPin] == pin) { + uint8_t _pinLSBByte = aPin * 2; + uint8_t _pinMSBByte = _pinLSBByte + 1; + return (_analogueInputStates[_pinMSBByte] << 8) + _analogueInputStates[_pinLSBByte]; + } + } + return -1; // pin not found in table + } + + void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override { + uint8_t servoBuffer[7]; + uint8_t responseBuffer[1]; + + if (_deviceState == DEVSTATE_FAILED) return; + int pin = vpin - _firstVpin; + servoBuffer[0] = EXIOWRAN; + servoBuffer[1] = (uint8_t) _nodeID; + servoBuffer[2] = (uint8_t) pin; + servoBuffer[3] = (uint8_t) value & 0xFF; + servoBuffer[4] = (uint8_t) value >> 8; + servoBuffer[5] = (uint8_t) profile; + servoBuffer[6] = (uint8_t) duration & 0xFF; + servoBuffer[7] = (uint8_t) duration >> 8; + bus->_busy = true; + bus->updateCrc(servoBuffer,8); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); + bus->_serialD->write(servoBuffer, 10); + bus->_serialD->flush(); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); + unsigned long startMillis = millis(); + while (!bus->_serialD->available()) { + if (millis() - startMillis >= 500) return; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (bus->_serialD->available()) { + startMicros = micros(); + responseBuffer[len] = bus->_serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + bus->_busy = false; + if (!bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)) { + DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) _nodeID); + //_deviceState = DEVSTATE_FAILED; + } else { + if (!bus->testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); + if (responseBuffer[0] != EXIORDY) { + DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a servo/PWM pin"), pin); + } + } + } + + uint8_t getBusNumber() { + return _busNo; + } + + void _display() override { + 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("")); + } + + +}; + +/********************************************************************** + * RSproto class + * + * This encapsulates the properties state of the bus and the + * transmission and reception of data across that bus. Each RSproto + * object owns a set of RSprotonode objects which represent the nodes + * attached to that bus. + **********************************************************************/ +class RSproto : public IODevice { +private: + // Here we define the device-specific variables. + uint8_t _busNo; + unsigned long _baud; + unsigned long _cycleStartTime = 0; + unsigned long _timeoutStart = 0; + unsigned long _cycleTime; // target time between successive read/write cycles, microseconds + unsigned long _timeoutPeriod; // timeout on read responses, in microseconds. + unsigned long _currentMicros; // last value of micros() from _loop function. + 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 RSproto *_busList; // linked list of defined bus instances + bool waitReceive = false; + int _waitCounter = 0; + int _waitCounterB = 0; + int _waitA; + unsigned long _charTimeout; + unsigned long _frameTimeout; + enum {RDS_IDLE, RDS_DIGITAL, RDS_ANALOGUE}; // Read operation states + uint8_t _readState = RDS_IDLE; + + unsigned long _lastDigitalRead = 0; + 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 + + // EX-IOExpander protocol flags + enum { + EXIOINIT = 0xE0, // Flag to initialise setup procedure + EXIORDY = 0xE1, // Flag we have completed setup procedure, also for EX-IO to ACK setup + EXIODPUP = 0xE2, // Flag we're sending digital pin pullup configuration + EXIOVER = 0xE3, // Flag to get version + EXIORDAN = 0xE4, // Flag to read an analogue input + EXIOWRD = 0xE5, // Flag for digital write + EXIORDD = 0xE6, // Flag to read digital input + EXIOENAN = 0xE7, // Flag to enable an analogue pin + EXIOINITA = 0xE8, // Flag we're receiving analogue pin mappings + EXIOPINS = 0xE9, // Flag we're receiving pin counts for buffers + EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM) + EXIOERR = 0xEF, // Flag we've received an error + }; + uint16_t _calculateCrc(uint8_t *buf, uint16_t len); + + RSprotonode *_nodeListStart = NULL, *_nodeListEnd = NULL; + RSprotonode *_currentNode = NULL; + uint8_t _exceptionResponse = 0; + uint8_t getExceptionResponse(); + uint16_t _receiveDataIndex = 0; // Index of next data byte to be received. + RSproto *_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; + } + +public: + int _CommMode = 0; + int _opperation = 0; + uint16_t _pullup; + uint16_t _pin; + int8_t _txPin; + bool _busy = false; + + int taskCnt = 0; + HardwareSerial *_serialD; + bool testAndStripMasterFlag(uint8_t *buf) { + if (buf[0] != 0xFF) return false; // why did we not get a master flag? bad node? + for (int i = 0; i < sizeof(buf)-1; i++) buf[i] = buf[i+1]; // shift array to begining + return true; + } + + + void updateCrc(uint8_t *buf, uint16_t len); + bool crcGood(uint8_t *buf, uint16_t len); + void clearRxBuffer(); + static void create(HardwareSerial& serial, unsigned long baud, uint16_t cycleTimeMS=500, int8_t txPin=-1, int waitA=10) { + new RSproto(serial, baud, cycleTimeMS, txPin, waitA); + } + + // Device-specific initialisation + void _begin() override { + _serialD->begin(_baud, SERIAL_8N1); + unsigned long bitsPerChar = 10; + if (_baud <= 19200) { + _charTimeout = (bitsPerChar * 2500000) / _baud; + _frameTimeout = (bitsPerChar * 4500000) / _baud; + } + else { + _charTimeout = (bitsPerChar * 1000000) / _baud + 750; + _frameTimeout = (bitsPerChar * 1000000) / _baud + 1750; + } + clearRxBuffer(); + #if defined(RSproto_STM_OK) + pinMode(RSproto_STM_OK, OUTPUT); + ArduinoPins::fastWriteDigital(RSproto_STM_OK,LOW); + #endif + #if defined(RSproto_STM_FAIL) + pinMode(RSproto_STM_FAIL, OUTPUT); + ArduinoPins::fastWriteDigital(RSproto_STM_FAIL,LOW); + #endif + #if defined(RSproto_STM_COMM) + pinMode(RSproto_STM_COMM, OUTPUT); + ArduinoPins::fastWriteDigital(RSproto_STM_COMM,LOW); + #endif + + #if defined(DIAG_IO) + _display(); + #endif + } + + + // Loop function (overriding IODevice::_loop(unsigned long)) + void _loop(unsigned long currentMicros) override; + + // Display information about the device + void _display() override { + DIAG(F("EX-IOExpander485 Configured on Vpins:%d-%d %S"), _firstVpin, _firstVpin+_nPins-1, + _deviceState == DEVSTATE_FAILED ? F("OFFLINE") : F("OK")); + } + + // Locate RSprotonode object with specified nodeID. + RSprotonode *findNode(uint8_t nodeID) { + for (RSprotonode *node = _nodeListStart; node != NULL; node = node->getNext()) { + if (node->getNodeID() == nodeID) + return node; + } + return NULL; + } + + + // Add new RSprotonode to the list of nodes for this bus. + void addNode(RSprotonode *newNode) { + if (!_nodeListStart) + _nodeListStart = newNode; + if (!_nodeListEnd) + _nodeListEnd = newNode; + else + _nodeListEnd->setNext(newNode); + //DIAG(F("RSproto: 260h nodeID:%d _nodeListStart:%d _nodeListEnd:%d"), newNode, _nodeListStart, _nodeListEnd); + } + +protected: + RSproto(HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA); + +public: + + uint8_t getBusNumber() { + return _busNo; + } + RSproto *getNext() { + return _nextBus; + } + static RSproto *findBus(uint8_t busNo) { + for (RSproto *bus = _busList; bus != NULL; bus = bus->getNext()) { + if (bus->getBusNumber() == busNo) + return bus; + } + return NULL; + } +}; + + +#endif // IO_RSproto_H \ No newline at end of file From e4ea6e56335c25ee6884d708fd5c7695c27e9970 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Fri, 13 Dec 2024 16:48:53 -0500 Subject: [PATCH 48/73] saving --- IO_RS485.cpp | 427 ------------------------------------- IO_RS485.h | 564 ------------------------------------------------- IO_RSproto.cpp | 250 ++++++++++++++++++++++ IO_RSproto.h | 2 +- 4 files changed, 251 insertions(+), 992 deletions(-) delete mode 100644 IO_RS485.cpp delete mode 100644 IO_RS485.h create mode 100644 IO_RSproto.cpp diff --git a/IO_RS485.cpp b/IO_RS485.cpp deleted file mode 100644 index ccb3a10f..00000000 --- a/IO_RS485.cpp +++ /dev/null @@ -1,427 +0,0 @@ -/* - * © 2024, Travis Farmer. All rights reserved. - * © 2024, Chris Bulliner. All rights reserved. https://github.com/CMB27 - * - * This file is part of DCC++EX API - * - * This is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * It is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CommandStation. If not, see . - */ - -#include "IO_RS485.h" -#include "defines.h" - -/************************************************************ - * 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; -} - -/* -= 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); - if (aduCrc == calculatedCrc) return true; - else return false; -} - -/* -= 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++) { - value ^= (uint16_t)buf[i]; - for (uint8_t j = 0; j < 8; j++) { - bool lsb = value & 1; - value >>= 1; - if (lsb == true) value ^= 0xA001; - } - } - return value; -} - -/* -= clearRxBuffer =- -// -// BLOCKING method to empty stray data in RX buffer -*/ -void RS485::clearRxBuffer() { - unsigned long startMicros = micros(); - do { - if (_serialD->available() > 0) { - startMicros = micros(); - _serialD->read(); - } - } while (micros() - startMicros < _frameTimeout); -} - -/* -= _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; - - if (_currentNode == NULL) { - _currentNode = _nodeListStart; - - } - - if (_currentMicros - _cycleStartTime < _cycleTime) return; - _cycleStartTime = _currentMicros; - if (_currentNode == NULL) return; - - bool flagOK = true; -#if defined(RS485_STM_COMM) - ArduinoPins::fastWriteDigital(RS485_STM_COMM,HIGH); -#endif - -if (taskCnt > 0) { - // run through tasks - if (!waitReceive) getNextTask(taskData); - switch((int) taskData[0]) { - case 0: - // protection for pulling empty task - break; - case 1: // configure pin - if (taskData[4] == (int*) CONFIGURE_INPUT) { - 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,4); - if (waitReceive == false) { - if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); - _serialD->write(outBuffer, 6); - _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; - } - waitReceive = false; - uint16_t len = 0; - unsigned long startMicros = micros(); - do { - if (_serialD->available()) { - startMicros = micros(); - responseBuffer[len] = _serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - if (crcGood(responseBuffer,sizeof(responseBuffer)-2)) { - if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); - if (responseBuffer[0] == EXIORDY) { - } else { - DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), (int)taskData[3]); - } - } 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 - // means increasing the RAM requirement of every HAL device driver, whether it's relevant - // to the driver or not. - } - break; - case 2: // configure analog in - uint8_t commandBuffer[5] = {EXIOENAN, (uint8_t) taskData[0], (uint8_t) taskData[3]}; - uint8_t responseBuffer[3]; - updateCrc(commandBuffer,3); - if (waitReceive == false) { - if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); - _serialD->write(commandBuffer, 5); - _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(); - responseBuffer[len] = _serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - - if (crcGood(responseBuffer,sizeof(responseBuffer)-2)) { - if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); - if (responseBuffer[0] != EXIORDY) { - 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; - } - break; - case 3: // write pin - uint8_t digitalOutBuffer[6]; - uint8_t responseBuffer[3]; - digitalOutBuffer[0] = EXIOWRD; - digitalOutBuffer[1] = (uint8_t) taskData[0]; - digitalOutBuffer[2] = (uint8_t) taskData[3]; - digitalOutBuffer[3] = (uint8_t) taskData[4]; - updateCrc(digitalOutBuffer,4); - if (waitReceive == false) { - if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); - _serialD->write(digitalOutBuffer, 6); - _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(); - responseBuffer[len] = _serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - if (crcGood(responseBuffer,sizeof(responseBuffer)-2)) { - if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); - if (responseBuffer[0] != EXIORDY) { - 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; - } - break; - case 4: - uint8_t servoBuffer[10]; - uint8_t responseBuffer[3]; -#ifdef DIAG_IO - 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; - servoBuffer[1] = (uint8_t) taskData[0]; - servoBuffer[2] = (uint8_t) taskData[3]; - servoBuffer[3] = (uint8_t) taskData[4] & 0xFF; - servoBuffer[4] = (uint8_t) taskData[4] >> 8; - servoBuffer[5] = (uint8_t) taskData[5]; - servoBuffer[6] = (uint8_t) taskData[6] & 0xFF; - servoBuffer[7] = (uint8_t) taskData[6] >> 8; - updateCrc(servoBuffer,8); - if (waitReceive == false) { - if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, HIGH); - _serialD->write(servoBuffer, 10); - _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(); - responseBuffer[len] = _serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - if (!crcGood(responseBuffer,sizeof(responseBuffer)-2)) { - DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) taskData[0]); - flagOK = false; - //_deviceState = DEVSTATE_FAILED; - } else { - if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); - if (responseBuffer[0] != EXIORDY) { - 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 - 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 (!testAndStripMasterFlag(_currentNode->_digitalInputStates)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); - 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 (!testAndStripMasterFlag(_currentNode->_digitalInputStates)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_currentNode->getNodeID()); - if (!waitReceive) _refreshOperation = 0; - _lastAnalogueRead = currentMicros; - _readState = RDS_ANALOGUE; - } - break; - } - if(flagOK && !waitReceive) _currentNode = _currentNode->getNext(); -} - -#if defined(RS485_STM_OK) - if (flagOK == true) { - ArduinoPins::fastWriteDigital(RS485_STM_OK,HIGH); - } else { - ArduinoPins::fastWriteDigital(RS485_STM_OK,LOW); - } -#endif -#if defined(RS485_STM_FAIL) - if (flagOK == false) { - ArduinoPins::fastWriteDigital(RS485_STM_FAIL,HIGH); - } else { - ArduinoPins::fastWriteDigital(RS485_STM_FAIL,LOW); - } -#endif -#if defined(RS485_STM_COMM) - ArduinoPins::fastWriteDigital(RS485_STM_COMM,LOW); -#endif - -} - -// Link to chain of RS485 instances, left over from RS485 template. -RS485 *RS485::_busList = NULL; - - -/************************************************************ - * RS485node implementation - ************************************************************/ - -/* -= RS485node =- -// -// Constructor for RS485node object -*/ -RS485node::RS485node(VPIN firstVpin, int nPins, uint8_t nodeID) { - _firstVpin = firstVpin; - _nPins = nPins; - _busNo = 0; - _nodeID = nodeID; - if (_nodeID > 254) _nodeID = 254; - - // Add this device to HAL device list - IODevice::addDevice(this); - _display(); - // Add RS485node to RS485 object. - RS485 *bus = RS485::findBus(_busNo); - if (bus != NULL) { - bus->addNode(this); - return; - } - -} diff --git a/IO_RS485.h b/IO_RS485.h deleted file mode 100644 index bf64afdf..00000000 --- a/IO_RS485.h +++ /dev/null @@ -1,564 +0,0 @@ -/* - * © 2024, Travis Farmer. All rights reserved. - * © 2024, Chris Bulliner. All rights reserved. https://github.com/CMB27 - * - * This file is part of DCC++EX API - * - * This is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * It is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CommandStation. If not, see . - */ - -/* - * RS485 - * ======= - * To define a RS485, example syntax: - * RS485::create(serial, baud[, cycletime[, pin]]); - * - * 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) - * - * - * RS485Node - * ======== - * 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 - * nodeID = 0-254 - */ - -#ifndef IO_RS485_H -#define IO_RS485_H - -#include "IODevice.h" - -/********************************************************************** - * RS485node class - * - * This encapsulates the state associated with a single RS485 node, - * which includes the nodeID, number of discrete inputs and coils, and - * the states of the discrete inputs and coils. - **********************************************************************/ -class RS485node : public IODevice { -private: - uint8_t _busNo; - uint8_t _nodeID; - char _type; - RS485node *_next = NULL; - bool _initialised = false; - - // EX-IOExpander protocol flags - enum { - EXIOINIT = 0xE0, // Flag to initialise setup procedure - EXIORDY = 0xE1, // Flag we have completed setup procedure, also for EX-IO to ACK setup - EXIODPUP = 0xE2, // Flag we're sending digital pin pullup configuration - EXIOVER = 0xE3, // Flag to get version - EXIORDAN = 0xE4, // Flag to read an analogue input - EXIOWRD = 0xE5, // Flag for digital write - EXIORDD = 0xE6, // Flag to read digital input - EXIOENAN = 0xE7, // Flag to enable an analogue pin - EXIOINITA = 0xE8, // Flag we're receiving analogue pin mappings - EXIOPINS = 0xE9, // Flag we're receiving pin counts for buffers - EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM) - EXIOERR = 0xEF, // Flag we've received an error - }; - -public: - enum ProfileType : int { - Instant = 0, // Moves immediately between positions (if duration not specified) - UseDuration = 0, // Use specified duration - Fast = 1, // Takes around 500ms end-to-end - Medium = 2, // 1 second end-to-end - Slow = 3, // 2 seconds end-to-end - Bounce = 4, // For semaphores/turnouts with a bit of bounce!! - NoPowerOff = 0x80, // Flag to be ORed in to suppress power off after move. - }; - - uint8_t _numDigitalPins = 0; - uint8_t _numAnaloguePins = 0; - - uint8_t _majorVer = 0; - uint8_t _minorVer = 0; - uint8_t _patchVer = 0; - - uint8_t* _digitalInputStates = NULL; - uint8_t* _analogueInputStates = NULL; - uint8_t* _analogueInputBuffer = NULL; // buffer for I2C input transfers - 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 nodeID) { - if (checkNoOverlap(firstVpin, nPins)) new RS485node(firstVpin, nPins, nodeID); - } - RS485node(VPIN firstVpin, int nPins, uint8_t nodeID); - - uint8_t getNodeID() { - return _nodeID; - } - - RS485node *getNext() { - return _next; - } - void setNext(RS485node *node) { - _next = node; - } - bool isInitialised() { - return _initialised; - } - void setInitialised() { - _initialised = true; - } - - bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override { - if (paramCount != 1) return false; - int pin = vpin - _firstVpin; - int pin = vpin - _firstVpin; - RS485 *bus = RS485::findBus(_busNo); - int* param[] = {(int*)pin, (int*)configType, (int*)paramCount, (int*)params[0]}; - bus->addTask(_nodeID, 3, 4, param); - - } - - int _configureAnalogIn(VPIN vpin) override { - int pin = vpin - _firstVpin; - RS485 *bus = RS485::findBus(_busNo); - int* params[] = {(int*)pin}; - bus->addTask(_nodeID, 3, 1, params); - - return false; - } - - void _begin() override { - 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)}; - 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 (!bus->_serialD->available()) { - if (millis() - startMillis >= 500) return; - } - uint16_t len = 0; - unsigned long startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - receiveBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - if (receiveBuffer[1] == EXIOPINS && bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { - if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_nodeID); - _numDigitalPins = receiveBuffer[1]; - _numAnaloguePins = receiveBuffer[2]; - - // See if we already have suitable buffers assigned - if (_numDigitalPins>0) { - size_t digitalBytesNeeded = (_numDigitalPins + 7) / 8; - if (_digitalPinBytes < digitalBytesNeeded) { - // Not enough space, free any existing buffer and allocate a new one - if (_digitalPinBytes > 0) free(_digitalInputStates); - if ((_digitalInputStates = (byte*) calloc(digitalBytesNeeded, 1)) != NULL) { - _digitalPinBytes = digitalBytesNeeded; - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), _nodeID, digitalBytesNeeded); - _deviceState = DEVSTATE_FAILED; - _digitalPinBytes = 0; - return; - } - } - } - - if (_numAnaloguePins>0) { - size_t analogueBytesNeeded = _numAnaloguePins * 2; - if (_analoguePinBytes < analogueBytesNeeded) { - // Free any existing buffers and allocate new ones. - if (_analoguePinBytes > 0) { - free(_analogueInputBuffer); - free(_analogueInputStates); - free(_analoguePinMap); - } - _analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1); - _analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1); - _analoguePinMap = (uint8_t*) calloc(_numAnaloguePins, 1); - if (_analogueInputStates != NULL && - _analogueInputBuffer != NULL && - _analoguePinMap != NULL) { - _analoguePinBytes = analogueBytesNeeded; - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), _nodeID); - _deviceState = DEVSTATE_FAILED; - _analoguePinBytes = 0; - return; - } - } - } - } else { - 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; - 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 (!bus->_serialD->available()) { - if (millis() - startMillis >= 500) return; - } - uint16_t len = 0; - unsigned long startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - receiveBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - if (bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { - if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_nodeID); - for (int i = 0; i < _numAnaloguePins; i++) { - _analoguePinMap[i] = receiveBuffer[i]; - } - } - uint8_t versionBuffer[5]; - commandBuffer[0] = EXIOVER; - 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 (!bus->_serialD->available()) { - if (millis() - startMillis >= 500) return; - } - uint16_t len = 0; - unsigned long startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - versionBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - if (bus->crcGood(versionBuffer,sizeof(versionBuffer)-2)) { - if (!bus->testAndStripMasterFlag(versionBuffer)) DIAG(F("Foreign RS485 Device! no master flag from node %d"),_nodeID); - _majorVer = versionBuffer[0]; - _minorVer = versionBuffer[1]; - _patchVer = versionBuffer[2]; - DIAG(F("EX-IOExpander485 device found, node:%d, Version v%d.%d.%d"), _nodeID, _majorVer, _minorVer, _patchVer); - } -#ifdef DIAG_IO - _display(); -#endif - _initialised = false; - } - - - int _read(VPIN vpin) override { - if (_deviceState == DEVSTATE_FAILED) return 0; - int pin = vpin - _firstVpin; - uint8_t pinByte = pin / 8; - bool value = bitRead(_digitalInputStates[pinByte], pin - pinByte * 8); - return value; - } - - - void _write(VPIN vpin, int value) override { - if (_deviceState == DEVSTATE_FAILED) return; - int pin = vpin - _firstVpin; - RS485 *bus = RS485::findBus(_busNo); - int* params[] = {(int*)pin, (int*)value}; - bus->addTask(_nodeID, 3, 2, params); - } - - int _readAnalogue(VPIN vpin) override { - if (_deviceState == DEVSTATE_FAILED) return 0; - int pin = vpin - _firstVpin; - for (uint8_t aPin = 0; aPin < _numAnaloguePins; aPin++) { - if (_analoguePinMap[aPin] == pin) { - uint8_t _pinLSBByte = aPin * 2; - uint8_t _pinMSBByte = _pinLSBByte + 1; - return (_analogueInputStates[_pinMSBByte] << 8) + _analogueInputStates[_pinLSBByte]; - } - } - return -1; // pin not found in table - } - - void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override { - uint8_t servoBuffer[7]; - uint8_t responseBuffer[1]; - - if (_deviceState == DEVSTATE_FAILED) return; - int pin = vpin - _firstVpin; - RS485 *bus = RS485::findBus(_busNo); - int* params[] = {(int*)pin, (int*)value, (int*)profile, (int*)duration}; - bus->addTask(_nodeID, 3, 4, params); - } - - uint8_t getBusNumber() { - return _busNo; - } - - void _display() override { - 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("")); - } - - -}; - -/********************************************************************** - * RS485 class - * - * This encapsulates the properties state of the bus and the - * transmission and reception of data across that bus. Each RS485 - * object owns a set of RS485node objects which represent the nodes - * attached to that bus. - **********************************************************************/ -class RS485 : public IODevice { -private: - // Here we define the device-specific variables. - uint8_t _busNo; - unsigned long _baud; - unsigned long _cycleStartTime = 0; - unsigned long _timeoutStart = 0; - unsigned long _cycleTime; // target time between successive read/write cycles, microseconds - unsigned long _timeoutPeriod; // timeout on read responses, in microseconds. - unsigned long _currentMicros; // last value of micros() from _loop function. - 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* taskData[25]; - unsigned long _charTimeout; - unsigned long _frameTimeout; - enum {RDS_IDLE, RDS_DIGITAL, RDS_ANALOGUE}; // Read operation states - uint8_t _readState = RDS_IDLE; - - unsigned long _lastDigitalRead = 0; - 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 { - EXIOINIT = 0xE0, // Flag to initialise setup procedure - EXIORDY = 0xE1, // Flag we have completed setup procedure, also for EX-IO to ACK setup - EXIODPUP = 0xE2, // Flag we're sending digital pin pullup configuration - EXIOVER = 0xE3, // Flag to get version - EXIORDAN = 0xE4, // Flag to read an analogue input - EXIOWRD = 0xE5, // Flag for digital write - EXIORDD = 0xE6, // Flag to read digital input - EXIOENAN = 0xE7, // Flag to enable an analogue pin - EXIOINITA = 0xE8, // Flag we're receiving analogue pin mappings - EXIOPINS = 0xE9, // Flag we're receiving pin counts for buffers - EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM) - EXIOERR = 0xEF, // Flag we've received an error - }; - 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++) { - for (int j = 0; j < 25; j++) { - tasks[i][j] = tasks[i+1][j+1]; - } - } - taskCnt--; - } -public: - int _CommMode = 0; - int _opperation = 0; - uint16_t _pullup; - uint16_t _pin; - int8_t _txPin; - int taskCnt = 0; - HardwareSerial *_serialD; - bool testAndStripMasterFlag(uint8_t *buf) { - if (buf[0] != 0xFF) return false; // why did we not get a master flag? bad node? - for (int i = 0; i < sizeof(buf)-1; i++) buf[i] = buf[i+1]; // shift array to begining - return true; - } - void addTask(int nodeID, int taskNum, int paramCnt, int *param[]) { - taskCnt++; - tasks[taskCnt][0] = nodeID; - tasks[taskCnt][1] = taskNum; - tasks[taskCnt][2] = paramCnt; - switch(taskNum) { - case 0: - // empty task - case 1: // configure pin - tasks[taskCnt][3] = (int) param[0]; // pin - tasks[taskCnt][4] = (int) param[1]; // configtype - tasks[taskCnt][5] = (int) param[2]; // paramcount - for (int i=0; i < (int) param[2]; i++) { - tasks[taskCnt][i+6] = (int) param[i+3]; // params - } - break; - case 2: // configure analog in - tasks[taskCnt][3] = (int) param[0]; // pin - break; - case 3: // write pin - tasks[taskCnt][3] = (int) param[0]; // pin - tasks[taskCnt][4] = (int) param[1]; // value - break; - case 4: // write analog - tasks[taskCnt][3] = (int) param[0]; // pin - tasks[taskCnt][4] = (int) param[1]; // value - tasks[taskCnt][5] = (int) param[2]; // profile - tasks[taskCnt][6] = (int) param[3]; // duration - break; - } - } - - int getNextTask(int *buf[]) { - int paramCnt = 0; - for (int i = 0; i < 25; i++) { - if (i == 0) buf[i] = (int*) tasks[0][i]; // NodeID - if (i == 1) buf[i] = (int*) tasks[0][i]; // tasknum - else if (i == 2) paramCnt = tasks[0][i]; // paramcnt - else { - buf[i-1] = (int*) tasks[0][i]; - } - } - _moveTasks(); - } - - - void updateCrc(uint8_t *buf, uint16_t len); - bool crcGood(uint8_t *buf, uint16_t len); - void clearRxBuffer(); - 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); - } - - // Device-specific initialisation - void _begin() override { - _serialD->begin(_baud, SERIAL_8N1); - unsigned long bitsPerChar = 10; - if (_baud <= 19200) { - _charTimeout = (bitsPerChar * 2500000) / _baud; - _frameTimeout = (bitsPerChar * 4500000) / _baud; - } - else { - _charTimeout = (bitsPerChar * 1000000) / _baud + 750; - _frameTimeout = (bitsPerChar * 1000000) / _baud + 1750; - } - clearRxBuffer(); - #if defined(RS485_STM_OK) - pinMode(RS485_STM_OK, OUTPUT); - ArduinoPins::fastWriteDigital(RS485_STM_OK,LOW); - #endif - #if defined(RS485_STM_FAIL) - pinMode(RS485_STM_FAIL, OUTPUT); - ArduinoPins::fastWriteDigital(RS485_STM_FAIL,LOW); - #endif - #if defined(RS485_STM_COMM) - pinMode(RS485_STM_COMM, OUTPUT); - ArduinoPins::fastWriteDigital(RS485_STM_COMM,LOW); - #endif - - #if defined(DIAG_IO) - _display(); - #endif - } - - - // Loop function (overriding IODevice::_loop(unsigned long)) - void _loop(unsigned long currentMicros) override; - - // Display information about the device - void _display() override { - DIAG(F("EX-IOExpander485 Configured on Vpins:%d-%d %S"), _firstVpin, _firstVpin+_nPins-1, - _deviceState == DEVSTATE_FAILED ? F("OFFLINE") : F("OK")); - } - - // Locate RS485node object with specified nodeID. - RS485node *findNode(uint8_t nodeID) { - for (RS485node *node = _nodeListStart; node != NULL; node = node->getNext()) { - if (node->getNodeID() == nodeID) - return node; - } - return NULL; - } - - - // Add new RS485node to the list of nodes for this bus. - void addNode(RS485node *newNode) { - if (!_nodeListStart) - _nodeListStart = newNode; - if (!_nodeListEnd) - _nodeListEnd = newNode; - else - _nodeListEnd->setNext(newNode); - //DIAG(F("RS485: 260h nodeID:%d _nodeListStart:%d _nodeListEnd:%d"), newNode, _nodeListStart, _nodeListEnd); - } - -protected: - RS485(HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA); - -public: - - uint8_t getBusNumber() { - return _busNo; - } - - static RS485 *findBus(uint8_t busNo) { - for (RS485 *bus=_busList; bus!=NULL; bus=bus->_nextBus) { - if (bus->_busNo == busNo) return bus; - } - return NULL; - } -}; - - -#endif // IO_RS485_H \ No newline at end of file diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp new file mode 100644 index 00000000..beaed9e8 --- /dev/null +++ b/IO_RSproto.cpp @@ -0,0 +1,250 @@ +/* + * © 2024, Travis Farmer. All rights reserved. + * © 2024, Chris Bulliner. All rights reserved. https://github.com/CMB27 + * + * This file is part of DCC++EX API + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + +#include "IO_RSproto.h" +#include "defines.h" + +/************************************************************ + * RSproto implementation + ************************************************************/ + +// Constructor for RSproto +RSproto::RSproto(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 RSproto chain. + _nextBus = _busList; + _busList = this; +} + +/* -= updateCrc =- +// +// add the CRC value from _calculateCrc (2 bytes) to the buffer. +*/ +void RSproto::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 RSproto::crcGood(uint8_t *buf, uint16_t len) { + uint16_t aduCrc = buf[len] | (buf[len + 1] << 8); + uint16_t calculatedCrc = _calculateCrc(buf, len); + if (aduCrc == calculatedCrc) return true; + else return false; +} + +/* -= calculateCrc =- +// +// use bitwise XOR to calculate CRC into a 16-bit byte +*/ +uint16_t RSproto::_calculateCrc(uint8_t *buf, uint16_t len) { + uint16_t value = 0xFFFF; + for (uint16_t i = 0; i < len; i++) { + value ^= (uint16_t)buf[i]; + for (uint8_t j = 0; j < 8; j++) { + bool lsb = value & 1; + value >>= 1; + if (lsb == true) value ^= 0xA001; + } + } + return value; +} + +/* -= clearRxBuffer =- +// +// BLOCKING method to empty stray data in RX buffer +*/ +void RSproto::clearRxBuffer() { + unsigned long startMicros = micros(); + do { + if (_serialD->available() > 0) { + startMicros = micros(); + _serialD->read(); + } + } while (micros() - startMicros < _frameTimeout); +} + +/* -= _loop =- +// +// Main loop function for RSproto. +// 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 RSproto::_loop(unsigned long currentMicros) { + _currentMicros = currentMicros; + + if (_currentNode == NULL) { + _currentNode = _nodeListStart; + + } + + if (_currentMicros - _cycleStartTime < _cycleTime) return; + _cycleStartTime = _currentMicros; + if (_currentNode == NULL) return; + + bool flagOK = true; +#if defined(RSproto_STM_COMM) + ArduinoPins::fastWriteDigital(RSproto_STM_COMM,HIGH); +#endif +if (!_busy) { + memcpy(_currentNode->_analogueInputStates, _currentNode->_analogueInputBuffer, _currentNode->_analoguePinBytes); // Copy I2C input buffer to states + 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 (!testAndStripMasterFlag(_currentNode->_digitalInputStates)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_currentNode->getNodeID()); + 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 (!testAndStripMasterFlag(_currentNode->_digitalInputStates)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_currentNode->getNodeID()); + if (!waitReceive) _refreshOperation = 0; + _lastAnalogueRead = currentMicros; + _readState = RDS_ANALOGUE; + } + break; + if(flagOK && !waitReceive) _currentNode = _currentNode->getNext(); + } +} +#if defined(RSproto_STM_OK) + if (flagOK == true) { + ArduinoPins::fastWriteDigital(RSproto_STM_OK,HIGH); + } else { + ArduinoPins::fastWriteDigital(RSproto_STM_OK,LOW); + } +#endif +#if defined(RSproto_STM_FAIL) + if (flagOK == false) { + ArduinoPins::fastWriteDigital(RSproto_STM_FAIL,HIGH); + } else { + ArduinoPins::fastWriteDigital(RSproto_STM_FAIL,LOW); + } +#endif +#if defined(RSproto_STM_COMM) + ArduinoPins::fastWriteDigital(RSproto_STM_COMM,LOW); +#endif + +} + +// Link to chain of RSproto instances, left over from RSproto template. +RSproto *RSproto::_busList = NULL; + + +/************************************************************ + * RSprotonode implementation + ************************************************************/ + +/* -= RSprotonode =- +// +// Constructor for RSprotonode object +*/ +RSprotonode::RSprotonode(VPIN firstVpin, int nPins, uint8_t nodeID) { + _firstVpin = firstVpin; + _nPins = nPins; + _busNo = 0; + _nodeID = nodeID; + if (_nodeID > 254) _nodeID = 254; + + // Add this device to HAL device list + IODevice::addDevice(this); + _display(); + // Add RSprotonode to RSproto object. + RSproto *bus = RSproto::findBus(_busNo); + if (bus != NULL) { + bus->addNode(this); + return; + } + +} diff --git a/IO_RSproto.h b/IO_RSproto.h index 950f5b26..605254d7 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -59,7 +59,7 @@ class RSprotonode : public IODevice { char _type; RSprotonode *_next = NULL; bool _initialised = false; - RSproto* bus = NULL; + RSproto *bus = NULL; // EX-IOExpander protocol flags enum { EXIOINIT = 0xE0, // Flag to initialise setup procedure From f22daf091462e7057e988d1f4692a2cca3fea04f Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Fri, 13 Dec 2024 17:19:45 -0500 Subject: [PATCH 49/73] Compiles!! --- IO_RSproto.cpp | 320 ++++++++++++++++++++++++++++++++++++++++++++++++ IO_RSproto.h | 326 ++----------------------------------------------- 2 files changed, 331 insertions(+), 315 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index beaed9e8..711a5290 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -248,3 +248,323 @@ RSprotonode::RSprotonode(VPIN firstVpin, int nPins, uint8_t nodeID) { } } + +bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) { + if (paramCount != 1) return false; + int pin = vpin - _firstVpin; + + uint16_t pullup = params[0]; + uint8_t outBuffer[6] = {EXIODPUP, _nodeID, pin, pullup}; + uint8_t responseBuffer[3]; + bus->_busy = true; + bus->updateCrc(outBuffer,4); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); + bus->_serialD->write(outBuffer, 6); + bus->_serialD->flush(); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); + unsigned long startMillis = millis(); + while (!bus->_serialD->available()) { + if (millis() - startMillis > 500) return false; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (bus->_serialD->available()) { + startMicros = micros(); + responseBuffer[len] = bus->_serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + bus->_busy = false; + if (bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)) { + if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); + if (responseBuffer[0] == EXIORDY) { + } else { + DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); + } + } else { + DIAG(F("EX-IOExpander485 node %d CRC Error"), _nodeID); + } + + } + + int RSprotonode::_configureAnalogIn(VPIN vpin) { + int pin = vpin - _firstVpin; + //RSproto *mainrs = RSproto::findBus(_busNo); + uint8_t commandBuffer[5] = {EXIOENAN, (uint8_t) _nodeID, (uint8_t) pin}; + uint8_t responseBuffer[3]; + bus->_busy = true; + bus->updateCrc(commandBuffer,3); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); + bus->_serialD->write(commandBuffer, 5); + bus->_serialD->flush(); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); + unsigned long startMillis = millis(); + while (!bus->_serialD->available()) { + if (millis() - startMillis > 500) return 0; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (bus->_serialD->available()) { + startMicros = micros(); + responseBuffer[len] = bus->_serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + bus->_busy = false; + if (bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)) { + if (!bus->testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); + if (responseBuffer[0] != EXIORDY) { + DIAG(F("EX-IOExpander485: Vpin %u on node %d cannot be used as an analogue input pin"), (int) pin, (int) _nodeID); + } + } else { + DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) _nodeID); + } + return false; + } + +void RSprotonode::_begin() { + 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)}; + 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 (!bus->_serialD->available()) { + if (millis() - startMillis >= 500) return; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (bus->_serialD->available()) { + startMicros = micros(); + receiveBuffer[len] = bus->_serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + if (receiveBuffer[1] == EXIOPINS && bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { + if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); + _numDigitalPins = receiveBuffer[1]; + _numAnaloguePins = receiveBuffer[2]; + + // See if we already have suitable buffers assigned + if (_numDigitalPins>0) { + size_t digitalBytesNeeded = (_numDigitalPins + 7) / 8; + if (_digitalPinBytes < digitalBytesNeeded) { + // Not enough space, free any existing buffer and allocate a new one + if (_digitalPinBytes > 0) free(_digitalInputStates); + if ((_digitalInputStates = (byte*) calloc(digitalBytesNeeded, 1)) != NULL) { + _digitalPinBytes = digitalBytesNeeded; + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), _nodeID, digitalBytesNeeded); + _deviceState = DEVSTATE_FAILED; + _digitalPinBytes = 0; + return; + } + } + } + + if (_numAnaloguePins>0) { + size_t analogueBytesNeeded = _numAnaloguePins * 2; + if (_analoguePinBytes < analogueBytesNeeded) { + // Free any existing buffers and allocate new ones. + if (_analoguePinBytes > 0) { + free(_analogueInputBuffer); + free(_analogueInputStates); + free(_analoguePinMap); + } + _analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1); + _analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1); + _analoguePinMap = (uint8_t*) calloc(_numAnaloguePins, 1); + if (_analogueInputStates != NULL && + _analogueInputBuffer != NULL && + _analoguePinMap != NULL) { + _analoguePinBytes = analogueBytesNeeded; + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), _nodeID); + _deviceState = DEVSTATE_FAILED; + _analoguePinBytes = 0; + return; + } + } + } + } else { + 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; + 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 (!bus->_serialD->available()) { + if (millis() - startMillis >= 500) return; + } + len = 0; + startMicros = micros(); + do { + if (bus->_serialD->available()) { + startMicros = micros(); + receiveBuffer[len] = bus->_serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + if (bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { + if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); + for (int i = 0; i < _numAnaloguePins; i++) { + _analoguePinMap[i] = receiveBuffer[i]; + } + } + uint8_t versionBuffer[5]; + commandBuffer[0] = EXIOVER; + 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 (!bus->_serialD->available()) { + if (millis() - startMillis >= 500) return; + } + len = 0; + startMicros = micros(); + do { + if (bus->_serialD->available()) { + startMicros = micros(); + versionBuffer[len] = bus->_serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + if (bus->crcGood(versionBuffer,sizeof(versionBuffer)-2)) { + if (!bus->testAndStripMasterFlag(versionBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); + _majorVer = versionBuffer[0]; + _minorVer = versionBuffer[1]; + _patchVer = versionBuffer[2]; + DIAG(F("EX-IOExpander485 device found, node:%d, Version v%d.%d.%d"), _nodeID, _majorVer, _minorVer, _patchVer); + } +#ifdef DIAG_IO + _display(); +#endif + _initialised = false; + } + +int RSprotonode::_read(VPIN vpin) { + if (_deviceState == DEVSTATE_FAILED) return 0; + int pin = vpin - _firstVpin; + uint8_t pinByte = pin / 8; + bool value = bitRead(_digitalInputStates[pinByte], pin - pinByte * 8); + return value; + } +void RSprotonode::_write(VPIN vpin, int value) { + if (_deviceState == DEVSTATE_FAILED) return; + int pin = vpin - _firstVpin; + uint8_t digitalOutBuffer[6]; + uint8_t responseBuffer[3]; + digitalOutBuffer[0] = EXIOWRD; + digitalOutBuffer[1] = (uint8_t) _nodeID; + digitalOutBuffer[2] = (uint8_t) pin; + digitalOutBuffer[3] = (uint8_t) value; + bus->_busy = true; + bus->updateCrc(digitalOutBuffer,4); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); + bus->_serialD->write(digitalOutBuffer, 6); + bus->_serialD->flush(); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); + unsigned long startMillis = millis(); + while (!bus->_serialD->available()) { + if (millis() - startMillis >= 500) return; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (bus->_serialD->available()) { + startMicros = micros(); + responseBuffer[len] = bus->_serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + bus->_busy = false; + if (bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)) { + if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); + if (responseBuffer[0] != EXIORDY) { + DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital output pin"), pin); + } + } else { + DIAG(F("EX-IOExpander485 node %d CRC Error"), _nodeID); + } + } + + bool RSprotonode::testAndStripMasterFlag(uint8_t *buf) { + if (buf[0] != 0xFF) return false; // why did we not get a master flag? bad node? + for (int i = 0; i < sizeof(buf)-1; i++) buf[i] = buf[i+1]; // shift array to begining + return true; + } + int RSprotonode::_readAnalogue(VPIN vpin) { + if (_deviceState == DEVSTATE_FAILED) return 0; + int pin = vpin - _firstVpin; + for (uint8_t aPin = 0; aPin < _numAnaloguePins; aPin++) { + if (_analoguePinMap[aPin] == pin) { + uint8_t _pinLSBByte = aPin * 2; + uint8_t _pinMSBByte = _pinLSBByte + 1; + return (_analogueInputStates[_pinMSBByte] << 8) + _analogueInputStates[_pinLSBByte]; + } + } + return -1; // pin not found in table + } + + void RSprotonode::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) { + uint8_t servoBuffer[7]; + uint8_t responseBuffer[1]; + + if (_deviceState == DEVSTATE_FAILED) return; + int pin = vpin - _firstVpin; + servoBuffer[0] = EXIOWRAN; + servoBuffer[1] = (uint8_t) _nodeID; + servoBuffer[2] = (uint8_t) pin; + servoBuffer[3] = (uint8_t) value & 0xFF; + servoBuffer[4] = (uint8_t) value >> 8; + servoBuffer[5] = (uint8_t) profile; + servoBuffer[6] = (uint8_t) duration & 0xFF; + servoBuffer[7] = (uint8_t) duration >> 8; + bus->_busy = true; + bus->updateCrc(servoBuffer,8); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); + bus->_serialD->write(servoBuffer, 10); + bus->_serialD->flush(); + if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); + unsigned long startMillis = millis(); + while (!bus->_serialD->available()) { + if (millis() - startMillis >= 500) return; + } + uint16_t len = 0; + unsigned long startMicros = micros(); + do { + if (bus->_serialD->available()) { + startMicros = micros(); + responseBuffer[len] = bus->_serialD->read(); + len++; + } + } while (micros() - startMicros <= 500 && len < 256); + bus->_busy = false; + if (!bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)) { + DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) _nodeID); + //_deviceState = DEVSTATE_FAILED; + } else { + if (!bus->testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); + if (responseBuffer[0] != EXIORDY) { + DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a servo/PWM pin"), pin); + } + } + } \ No newline at end of file diff --git a/IO_RSproto.h b/IO_RSproto.h index 605254d7..f8aa1fe2 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -45,6 +45,8 @@ #include "IODevice.h" +class RSproto; + /********************************************************************** * RSprotonode class * @@ -59,7 +61,7 @@ class RSprotonode : public IODevice { char _type; RSprotonode *_next = NULL; bool _initialised = false; - RSproto *bus = NULL; + RSproto *bus; // EX-IOExpander protocol flags enum { EXIOINIT = 0xE0, // Flag to initialise setup procedure @@ -125,329 +127,23 @@ class RSprotonode : public IODevice { _initialised = true; } - bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override { - if (paramCount != 1) return false; - int pin = vpin - _firstVpin; - - uint16_t pullup = params[0]; - uint8_t outBuffer[6] = {EXIODPUP, _nodeID, pin, pullup}; - uint8_t responseBuffer[3]; - bus->_busy = true; - bus->updateCrc(outBuffer,4); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - bus->_serialD->write(outBuffer, 6); - bus->_serialD->flush(); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); - unsigned long startMillis = millis(); - while (!bus->_serialD->available()) { - if (millis() - startMillis > 500) return false; - } - uint16_t len = 0; - unsigned long startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - responseBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - bus->_busy = false; - if (bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)) { - if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - if (responseBuffer[0] == EXIORDY) { - } else { - DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); - } - } else { - DIAG(F("EX-IOExpander485 node %d CRC Error"), _nodeID); - } + bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override; - } + int _configureAnalogIn(VPIN vpin) override; - int _configureAnalogIn(VPIN vpin) override { - int pin = vpin - _firstVpin; - //RSproto *mainrs = RSproto::findBus(_busNo); - uint8_t commandBuffer[5] = {EXIOENAN, (uint8_t) _nodeID, (uint8_t) pin}; - uint8_t responseBuffer[3]; - bus->_busy = true; - bus->updateCrc(commandBuffer,3); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - bus->_serialD->write(commandBuffer, 5); - bus->_serialD->flush(); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); - unsigned long startMillis = millis(); - while (!bus->_serialD->available()) { - if (millis() - startMillis > 500) return 0; - } - uint16_t len = 0; - unsigned long startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - responseBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - bus->_busy = false; - if (bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)) { - if (!bus->testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - if (responseBuffer[0] != EXIORDY) { - DIAG(F("EX-IOExpander485: Vpin %u on node %d cannot be used as an analogue input pin"), (int) pin, (int) _nodeID); - } - } else { - DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) _nodeID); - } - return false; - } - - void _begin() override { - 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)}; - 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 (!bus->_serialD->available()) { - if (millis() - startMillis >= 500) return; - } - uint16_t len = 0; - unsigned long startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - receiveBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - if (receiveBuffer[1] == EXIOPINS && bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { - if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - _numDigitalPins = receiveBuffer[1]; - _numAnaloguePins = receiveBuffer[2]; - - // See if we already have suitable buffers assigned - if (_numDigitalPins>0) { - size_t digitalBytesNeeded = (_numDigitalPins + 7) / 8; - if (_digitalPinBytes < digitalBytesNeeded) { - // Not enough space, free any existing buffer and allocate a new one - if (_digitalPinBytes > 0) free(_digitalInputStates); - if ((_digitalInputStates = (byte*) calloc(digitalBytesNeeded, 1)) != NULL) { - _digitalPinBytes = digitalBytesNeeded; - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), _nodeID, digitalBytesNeeded); - _deviceState = DEVSTATE_FAILED; - _digitalPinBytes = 0; - return; - } - } - } - - if (_numAnaloguePins>0) { - size_t analogueBytesNeeded = _numAnaloguePins * 2; - if (_analoguePinBytes < analogueBytesNeeded) { - // Free any existing buffers and allocate new ones. - if (_analoguePinBytes > 0) { - free(_analogueInputBuffer); - free(_analogueInputStates); - free(_analoguePinMap); - } - _analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1); - _analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1); - _analoguePinMap = (uint8_t*) calloc(_numAnaloguePins, 1); - if (_analogueInputStates != NULL && - _analogueInputBuffer != NULL && - _analoguePinMap != NULL) { - _analoguePinBytes = analogueBytesNeeded; - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), _nodeID); - _deviceState = DEVSTATE_FAILED; - _analoguePinBytes = 0; - return; - } - } - } - } else { - 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; - 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 (!bus->_serialD->available()) { - if (millis() - startMillis >= 500) return; - } - len = 0; - startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - receiveBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - if (bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { - if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - for (int i = 0; i < _numAnaloguePins; i++) { - _analoguePinMap[i] = receiveBuffer[i]; - } - } - uint8_t versionBuffer[5]; - commandBuffer[0] = EXIOVER; - 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 (!bus->_serialD->available()) { - if (millis() - startMillis >= 500) return; - } - len = 0; - startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - versionBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - if (bus->crcGood(versionBuffer,sizeof(versionBuffer)-2)) { - if (!bus->testAndStripMasterFlag(versionBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - _majorVer = versionBuffer[0]; - _minorVer = versionBuffer[1]; - _patchVer = versionBuffer[2]; - DIAG(F("EX-IOExpander485 device found, node:%d, Version v%d.%d.%d"), _nodeID, _majorVer, _minorVer, _patchVer); - } -#ifdef DIAG_IO - _display(); -#endif - _initialised = false; - } + void _begin() override; - int _read(VPIN vpin) override { - if (_deviceState == DEVSTATE_FAILED) return 0; - int pin = vpin - _firstVpin; - uint8_t pinByte = pin / 8; - bool value = bitRead(_digitalInputStates[pinByte], pin - pinByte * 8); - return value; - } + int _read(VPIN vpin) override; - void _write(VPIN vpin, int value) override { - if (_deviceState == DEVSTATE_FAILED) return; - int pin = vpin - _firstVpin; - uint8_t digitalOutBuffer[6]; - uint8_t responseBuffer[3]; - digitalOutBuffer[0] = EXIOWRD; - digitalOutBuffer[1] = (uint8_t) _nodeID; - digitalOutBuffer[2] = (uint8_t) pin; - digitalOutBuffer[3] = (uint8_t) value; - bus->_busy = true; - bus->updateCrc(digitalOutBuffer,4); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - bus->_serialD->write(digitalOutBuffer, 6); - bus->_serialD->flush(); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); - unsigned long startMillis = millis(); - while (!bus->_serialD->available()) { - if (millis() - startMillis >= 500) return; - } - uint16_t len = 0; - unsigned long startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - responseBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - bus->_busy = false; - if (bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)) { - if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - if (responseBuffer[0] != EXIORDY) { - DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital output pin"), pin); - } - } else { - DIAG(F("EX-IOExpander485 node %d CRC Error"), _nodeID); - } - } + void _write(VPIN vpin, int value) override; - bool testAndStripMasterFlag(uint8_t *buf) { - if (buf[0] != 0xFF) return false; // why did we not get a master flag? bad node? - for (int i = 0; i < sizeof(buf)-1; i++) buf[i] = buf[i+1]; // shift array to begining - return true; - } + bool testAndStripMasterFlag(uint8_t *buf); - int _readAnalogue(VPIN vpin) override { - if (_deviceState == DEVSTATE_FAILED) return 0; - int pin = vpin - _firstVpin; - for (uint8_t aPin = 0; aPin < _numAnaloguePins; aPin++) { - if (_analoguePinMap[aPin] == pin) { - uint8_t _pinLSBByte = aPin * 2; - uint8_t _pinMSBByte = _pinLSBByte + 1; - return (_analogueInputStates[_pinMSBByte] << 8) + _analogueInputStates[_pinLSBByte]; - } - } - return -1; // pin not found in table - } + int _readAnalogue(VPIN vpin) override; - void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override { - uint8_t servoBuffer[7]; - uint8_t responseBuffer[1]; - - if (_deviceState == DEVSTATE_FAILED) return; - int pin = vpin - _firstVpin; - servoBuffer[0] = EXIOWRAN; - servoBuffer[1] = (uint8_t) _nodeID; - servoBuffer[2] = (uint8_t) pin; - servoBuffer[3] = (uint8_t) value & 0xFF; - servoBuffer[4] = (uint8_t) value >> 8; - servoBuffer[5] = (uint8_t) profile; - servoBuffer[6] = (uint8_t) duration & 0xFF; - servoBuffer[7] = (uint8_t) duration >> 8; - bus->_busy = true; - bus->updateCrc(servoBuffer,8); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - bus->_serialD->write(servoBuffer, 10); - bus->_serialD->flush(); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); - unsigned long startMillis = millis(); - while (!bus->_serialD->available()) { - if (millis() - startMillis >= 500) return; - } - uint16_t len = 0; - unsigned long startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - responseBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - bus->_busy = false; - if (!bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)) { - DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) _nodeID); - //_deviceState = DEVSTATE_FAILED; - } else { - if (!bus->testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - if (responseBuffer[0] != EXIORDY) { - DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a servo/PWM pin"), pin); - } - } - } + void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override; uint8_t getBusNumber() { return _busNo; From a11ebb3ed89b3916e6d1da48e54e11a9b32de9fc Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sat, 14 Dec 2024 16:55:53 -0500 Subject: [PATCH 50/73] transmits but wont receive --- IO_RSproto.cpp | 233 +++++++++++++++++++++++-------------------------- IO_RSproto.h | 32 +++---- 2 files changed, 126 insertions(+), 139 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index 711a5290..b42365ea 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -60,6 +60,9 @@ void RSproto::updateCrc(uint8_t *buf, uint16_t len) { bool RSproto::crcGood(uint8_t *buf, uint16_t len) { uint16_t aduCrc = buf[len] | (buf[len + 1] << 8); uint16_t calculatedCrc = _calculateCrc(buf, len); +#if defined(IO_DIAG) + DIAG(F("CRC is %d Expected %d"),calculatedCrc, aduCrc); +#endif if (aduCrc == calculatedCrc) return true; else return false; } @@ -92,7 +95,7 @@ void RSproto::clearRxBuffer() { startMicros = micros(); _serialD->read(); } - } while (micros() - startMicros < _frameTimeout); + } while (micros() - startMicros < _frameTimeout || !_serialD->available()); } /* -= _loop =- @@ -117,19 +120,20 @@ void RSproto::_loop(unsigned long currentMicros) { #if defined(RSproto_STM_COMM) ArduinoPins::fastWriteDigital(RSproto_STM_COMM,HIGH); #endif -if (!_busy) { +if (nodesInitialized()) { memcpy(_currentNode->_analogueInputStates, _currentNode->_analogueInputBuffer, _currentNode->_analoguePinBytes); // Copy I2C input buffer to states 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(); + _currentNode->_readCommandBuffer[0] = _currentNode->getNodeID(); + _currentNode->_readCommandBuffer[1] = 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->write(initBuffer, 1); _serialD->flush(); if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, LOW); } @@ -141,14 +145,10 @@ if (!_busy) { } 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)) { + bool rxDone = false; + byte tmpByte; + len = _serialD->readBytesUntil(0xFE,_currentNode->_digitalInputStates, 25); + if (!true/*crcGood(_currentNode->_digitalInputStates,sizeof(_currentNode->_digitalInputStates)-2)*/) { DIAG(F("EX-IOExpander485 CRC error on node %d"), _currentNode->getNodeID()); flagOK = false; } @@ -161,12 +161,13 @@ if (!_busy) { 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(); + _currentNode->_readCommandBuffer[0] = _currentNode->getNodeID(); + _currentNode->_readCommandBuffer[1] = 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->write(initBuffer, 1); _serialD->flush(); if (_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(_txPin, LOW); } @@ -178,14 +179,10 @@ if (!_busy) { } 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)) { + bool rxDone = false; + byte tmpByte; + len = _serialD->readBytesUntil(0xFE,_currentNode->_analogueInputBuffer, 25); + if (!true/*crcGood(_currentNode->_digitalInputStates,sizeof(_currentNode->_digitalInputStates)-2)*/) { DIAG(F("EX-IOExpander485 CRC error on node %d"), _currentNode->getNodeID()); flagOK = false; } @@ -235,6 +232,8 @@ RSprotonode::RSprotonode(VPIN firstVpin, int nPins, uint8_t nodeID) { _nPins = nPins; _busNo = 0; _nodeID = nodeID; + bus = bus->findBus(0); + _serial = bus->_serialD; if (_nodeID > 254) _nodeID = 254; // Add this device to HAL device list @@ -254,29 +253,26 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun int pin = vpin - _firstVpin; uint16_t pullup = params[0]; - uint8_t outBuffer[6] = {EXIODPUP, _nodeID, pin, pullup}; + uint8_t outBuffer[6] = {_nodeID, EXIODPUP, pin, pullup}; uint8_t responseBuffer[3]; bus->_busy = true; bus->updateCrc(outBuffer,4); if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - bus->_serialD->write(outBuffer, 6); - bus->_serialD->flush(); + _serial->write(outBuffer, 6); + _serial->write(initBuffer, 1); + _serial->flush(); if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); unsigned long startMillis = millis(); - while (!bus->_serialD->available()) { + while (!_serial->available()) { if (millis() - startMillis > 500) return false; } uint16_t len = 0; unsigned long startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - responseBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); + bool rxDone = false; + byte tmpByte; + len = _serial->readBytesUntil(0xFE,responseBuffer, 25); bus->_busy = false; - if (bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)) { + if (true/*bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)*/) { if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); if (responseBuffer[0] == EXIORDY) { } else { @@ -291,29 +287,26 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun int RSprotonode::_configureAnalogIn(VPIN vpin) { int pin = vpin - _firstVpin; //RSproto *mainrs = RSproto::findBus(_busNo); - uint8_t commandBuffer[5] = {EXIOENAN, (uint8_t) _nodeID, (uint8_t) pin}; + uint8_t commandBuffer[5] = {(uint8_t) _nodeID, EXIOENAN, (uint8_t) pin}; uint8_t responseBuffer[3]; bus->_busy = true; bus->updateCrc(commandBuffer,3); if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - bus->_serialD->write(commandBuffer, 5); - bus->_serialD->flush(); + _serial->write(commandBuffer, 5); + _serial->write(initBuffer, 1); + _serial->flush(); if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); unsigned long startMillis = millis(); - while (!bus->_serialD->available()) { + while (!_serial->available()) { if (millis() - startMillis > 500) return 0; } uint16_t len = 0; unsigned long startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - responseBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); + bool rxDone = false; + byte tmpByte; + len = _serial->readBytesUntil(0xFE,responseBuffer, 25); bus->_busy = false; - if (bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)) { + if (true/*bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)*/) { if (!bus->testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); if (responseBuffer[0] != EXIORDY) { DIAG(F("EX-IOExpander485: Vpin %u on node %d cannot be used as an analogue input pin"), (int) pin, (int) _nodeID); @@ -325,31 +318,34 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun } void RSprotonode::_begin() { - if (bus->_txPin != VPIN_NONE) { - pinMode(bus->_txPin, OUTPUT); - ArduinoPins::fastWriteDigital(bus->_txPin, LOW); - } + //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)}; + + uint8_t commandBuffer[7] = {_nodeID, EXIOINIT, (uint8_t)_nPins, (_firstVpin & (uint8_t)0xFF), (_firstVpin >> (uint8_t)8)}; 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); + + //_serial->begin(115200); + //ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); + digitalWrite(bus->_txPin,HIGH); unsigned long startMillis = millis(); - while (!bus->_serialD->available()) { + + _serial->write(commandBuffer, 7); + _serial->write(initBuffer, 1); + _serial->flush(); + digitalWrite(bus->_txPin,LOW); + //ArduinoPins::fastWriteDigital(bus->_txPin, LOW); + startMillis = millis(); + while (!_serial->available()) { if (millis() - startMillis >= 500) return; } uint16_t len = 0; unsigned long startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - receiveBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - if (receiveBuffer[1] == EXIOPINS && bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { + byte tmpByte; + bool rxDone = false; + len = _serial->readBytesUntil(0xFE,receiveBuffer, 25); + DIAG(F("rxcode:%d from node"),receiveBuffer[1]); + if (receiveBuffer[1] == EXIOPINS /*&& bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-3)*/) { if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); _numDigitalPins = receiveBuffer[1]; _numAnaloguePins = receiveBuffer[2]; @@ -364,7 +360,7 @@ void RSprotonode::_begin() { _digitalPinBytes = digitalBytesNeeded; } else { DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), _nodeID, digitalBytesNeeded); - _deviceState = DEVSTATE_FAILED; + //_deviceState = DEVSTATE_FAILED; _digitalPinBytes = 0; return; } @@ -389,7 +385,7 @@ void RSprotonode::_begin() { _analoguePinBytes = analogueBytesNeeded; } else { DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), _nodeID); - _deviceState = DEVSTATE_FAILED; + //_deviceState = DEVSTATE_FAILED; _analoguePinBytes = 0; return; } @@ -397,68 +393,63 @@ void RSprotonode::_begin() { } } else { DIAG(F("EX-IOExpander485 node:%d ERROR configuring device (CRC: %s)"), _nodeID, bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)? "PASS":"FAIL"); - _deviceState = DEVSTATE_FAILED; + //_deviceState = DEVSTATE_FAILED; return; } - commandBuffer[0] = EXIOINITA; - commandBuffer[1] = _nodeID; + commandBuffer[0] = _nodeID; + commandBuffer[1] = EXIOINITA; bus->updateCrc(commandBuffer,2); if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - bus->_serialD->write(commandBuffer, 4); - bus->_serialD->flush(); + _serial->write(commandBuffer, 4); + _serial->write(initBuffer, 1); + _serial->flush(); if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); startMillis = millis(); - while (!bus->_serialD->available()) { + while (!_serial->available()) { if (millis() - startMillis >= 500) return; } len = 0; startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - receiveBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - if (bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)) { + rxDone = false; + len = _serial->readBytesUntil(0xFE,receiveBuffer, 25); + + DIAG(F("rxcode:%d from node"),receiveBuffer[1]); + if (true/*bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)*/) { if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); for (int i = 0; i < _numAnaloguePins; i++) { _analoguePinMap[i] = receiveBuffer[i]; } } uint8_t versionBuffer[5]; - commandBuffer[0] = EXIOVER; - commandBuffer[1] = _nodeID; + commandBuffer[0] = _nodeID; + commandBuffer[1] = EXIOVER; bus->updateCrc(commandBuffer,2); if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - bus->_serialD->write(commandBuffer, 4); - bus->_serialD->flush(); + _serial->write(commandBuffer, 4); + _serial->write(initBuffer, 1); + _serial->flush(); if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); startMillis = millis(); - while (!bus->_serialD->available()) { + while (!_serial->available()) { if (millis() - startMillis >= 500) return; } len = 0; startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - versionBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); - if (bus->crcGood(versionBuffer,sizeof(versionBuffer)-2)) { + rxDone = false; + len = _serial->readBytesUntil(0xFE,receiveBuffer, 25); + + DIAG(F("rxcode:%d.%d.%d from node"),versionBuffer[1],versionBuffer[2],versionBuffer[3]); + if (true/*bus->crcGood(versionBuffer,sizeof(versionBuffer)-2)*/) { if (!bus->testAndStripMasterFlag(versionBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - _majorVer = versionBuffer[0]; - _minorVer = versionBuffer[1]; - _patchVer = versionBuffer[2]; + _majorVer = versionBuffer[1]; + _minorVer = versionBuffer[2]; + _patchVer = versionBuffer[3]; DIAG(F("EX-IOExpander485 device found, node:%d, Version v%d.%d.%d"), _nodeID, _majorVer, _minorVer, _patchVer); } #ifdef DIAG_IO _display(); #endif - _initialised = false; - } +} int RSprotonode::_read(VPIN vpin) { if (_deviceState == DEVSTATE_FAILED) return 0; @@ -472,31 +463,28 @@ void RSprotonode::_write(VPIN vpin, int value) { int pin = vpin - _firstVpin; uint8_t digitalOutBuffer[6]; uint8_t responseBuffer[3]; - digitalOutBuffer[0] = EXIOWRD; - digitalOutBuffer[1] = (uint8_t) _nodeID; + digitalOutBuffer[0] = (uint8_t) _nodeID; + digitalOutBuffer[1] = EXIOWRD; digitalOutBuffer[2] = (uint8_t) pin; digitalOutBuffer[3] = (uint8_t) value; bus->_busy = true; bus->updateCrc(digitalOutBuffer,4); if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - bus->_serialD->write(digitalOutBuffer, 6); - bus->_serialD->flush(); + _serial->write(digitalOutBuffer, 6); + _serial->write(initBuffer, 1); + _serial->flush(); if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); unsigned long startMillis = millis(); - while (!bus->_serialD->available()) { + while (!_serial->available()) { if (millis() - startMillis >= 500) return; } uint16_t len = 0; unsigned long startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - responseBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); + bool rxDone = false; + byte tmpByte; + len = _serial->readBytesUntil(0xFE,responseBuffer, 25); bus->_busy = false; - if (bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)) { + if (true/*bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)*/) { if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); if (responseBuffer[0] != EXIORDY) { DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital output pin"), pin); @@ -530,8 +518,8 @@ void RSprotonode::_write(VPIN vpin, int value) { if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; - servoBuffer[0] = EXIOWRAN; - servoBuffer[1] = (uint8_t) _nodeID; + servoBuffer[0] = (uint8_t) _nodeID; + servoBuffer[1] = EXIOWRAN; servoBuffer[2] = (uint8_t) pin; servoBuffer[3] = (uint8_t) value & 0xFF; servoBuffer[4] = (uint8_t) value >> 8; @@ -541,24 +529,21 @@ void RSprotonode::_write(VPIN vpin, int value) { bus->_busy = true; bus->updateCrc(servoBuffer,8); if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - bus->_serialD->write(servoBuffer, 10); - bus->_serialD->flush(); + _serial->write(servoBuffer, 10); + _serial->write(initBuffer, 1); + _serial->flush(); if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); unsigned long startMillis = millis(); - while (!bus->_serialD->available()) { + while (!_serial->available()) { if (millis() - startMillis >= 500) return; } uint16_t len = 0; unsigned long startMicros = micros(); - do { - if (bus->_serialD->available()) { - startMicros = micros(); - responseBuffer[len] = bus->_serialD->read(); - len++; - } - } while (micros() - startMicros <= 500 && len < 256); + bool rxDone = false; + byte tmpByte; + len = _serial->readBytesUntil(0xFE,responseBuffer, 25); bus->_busy = false; - if (!bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)) { + if (!true/*bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)*/) { DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) _nodeID); //_deviceState = DEVSTATE_FAILED; } else { diff --git a/IO_RSproto.h b/IO_RSproto.h index f8aa1fe2..fd3d05aa 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -62,6 +62,7 @@ class RSprotonode : public IODevice { RSprotonode *_next = NULL; bool _initialised = false; RSproto *bus; + HardwareSerial* _serial; // EX-IOExpander protocol flags enum { EXIOINIT = 0xE0, // Flag to initialise setup procedure @@ -104,7 +105,8 @@ class RSprotonode : public IODevice { 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; - + uint8_t initBuffer[1] = {0xFE}; + bool _initalized; static void create(VPIN firstVpin, int nPins, uint8_t nodeID) { if (checkNoOverlap(firstVpin, nPins)) new RSprotonode(firstVpin, nPins, nodeID); } @@ -168,7 +170,7 @@ class RSproto : public IODevice { private: // Here we define the device-specific variables. uint8_t _busNo; - unsigned long _baud; + unsigned long _cycleStartTime = 0; unsigned long _timeoutStart = 0; unsigned long _cycleTime; // target time between successive read/write cycles, microseconds @@ -233,11 +235,12 @@ class RSproto : public IODevice { uint16_t _pin; int8_t _txPin; bool _busy = false; - + unsigned long _baud; int taskCnt = 0; HardwareSerial *_serialD; + uint8_t initBuffer[1] = {0xFE}; bool testAndStripMasterFlag(uint8_t *buf) { - if (buf[0] != 0xFF) return false; // why did we not get a master flag? bad node? + if (buf[0] != 0xFD) return false; // why did we not get a master flag? bad node? for (int i = 0; i < sizeof(buf)-1; i++) buf[i] = buf[i+1]; // shift array to begining return true; } @@ -252,17 +255,9 @@ class RSproto : public IODevice { // Device-specific initialisation void _begin() override { + _serialD->begin(_baud, SERIAL_8N1); - unsigned long bitsPerChar = 10; - if (_baud <= 19200) { - _charTimeout = (bitsPerChar * 2500000) / _baud; - _frameTimeout = (bitsPerChar * 4500000) / _baud; - } - else { - _charTimeout = (bitsPerChar * 1000000) / _baud + 750; - _frameTimeout = (bitsPerChar * 1000000) / _baud + 1750; - } - clearRxBuffer(); + _serialD->flush(); #if defined(RSproto_STM_OK) pinMode(RSproto_STM_OK, OUTPUT); ArduinoPins::fastWriteDigital(RSproto_STM_OK,LOW); @@ -300,7 +295,14 @@ class RSproto : public IODevice { return NULL; } - + bool nodesInitialized() { + bool retval = true; + for (RSprotonode *node = _nodeListStart; node != NULL; node = node->getNext()) { + if (node->_initalized == false) + retval = false; + } + return retval; + } // Add new RSprotonode to the list of nodes for this bus. void addNode(RSprotonode *newNode) { if (!_nodeListStart) From 2d1f4ca108ca24bf725f6566b9e09335740aed26 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sun, 15 Dec 2024 12:18:52 -0500 Subject: [PATCH 51/73] saving current --- IO_RSproto.cpp | 474 ++++++++++++++++++++++++------------------------- IO_RSproto.h | 106 ++++++++--- 2 files changed, 316 insertions(+), 264 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index b42365ea..6779438e 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -21,43 +21,39 @@ #include "IO_RSproto.h" #include "defines.h" -/************************************************************ - * RSproto implementation - ************************************************************/ +static const byte PAYLOAD_FALSE = 0; +static const byte PAYLOAD_NORMAL = 1; +static const byte PAYLOAD_STRING = 2; -// Constructor for RSproto -RSproto::RSproto(HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA) { - _baud = baud; - _serialD = &serial; - _txPin = txPin; - _busNo = 0; +taskBuffer::taskBuffer(Stream * myserial) +{ + // constructor + next=first; + first=this; + serial = myserial; +} - _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 RSproto chain. - _nextBus = _busList; - _busList = this; +taskBuffer::~taskBuffer() +{ + // destructor } /* -= updateCrc =- // // add the CRC value from _calculateCrc (2 bytes) to the buffer. */ -void RSproto::updateCrc(uint8_t *buf, uint16_t len) { +void taskBuffer::updateCrc(uint8_t *crcBuf, uint8_t *buf, uint16_t len) { + if (sizeof(crcBuf) != 2) return; uint16_t crc = _calculateCrc(buf, len); - buf[len] = lowByte(crc); - buf[len + 1] = highByte(crc); + crcBuf[0] = lowByte(crc); + crcBuf[1] = highByte(crc); } /* -= crcGood =- // // return TRUE if CRC matched between buffer copy, and calculated. */ -bool RSproto::crcGood(uint8_t *buf, uint16_t len) { +bool taskBuffer::crcGood(uint8_t *buf, uint16_t len) { uint16_t aduCrc = buf[len] | (buf[len + 1] << 8); uint16_t calculatedCrc = _calculateCrc(buf, len); #if defined(IO_DIAG) @@ -71,7 +67,7 @@ bool RSproto::crcGood(uint8_t *buf, uint16_t len) { // // use bitwise XOR to calculate CRC into a 16-bit byte */ -uint16_t RSproto::_calculateCrc(uint8_t *buf, uint16_t len) { +uint16_t taskBuffer::_calculateCrc(uint8_t *buf, uint16_t len) { uint16_t value = 0xFFFF; for (uint16_t i = 0; i < len; i++) { value ^= (uint16_t)buf[i]; @@ -84,6 +80,209 @@ uint16_t RSproto::_calculateCrc(uint8_t *buf, uint16_t len) { return value; } +void taskBuffer::doCommand(uint8_t *commandBuffer, int commandSize) { + for (taskBuffer * t=first;t;t=t->next) t->doCommand2(nodeID,commandBuffer,commandSize); +} + +void taskBuffer::doCommand2(uint8_t *commandBuffer, int commandSize) { + // process commands here to be sent + uint8_t crcBuffer[2]; + updateCrc(crcBuffer, commandBuffer, commandSize); + + //_serial->begin(115200); + //ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); + digitalWrite(_txPin,HIGH); + unsigned long startMillis = millis(); + + serial->write(commandBuffer, 7); + serial->write(endChar, 1); + serial->flush(); + digitalWrite(_txPin,LOW); +} + +void taskBuffer::init(unsigned long baud, uint16_t cycleTimeMS, int8_t txPin) { +#ifdef RS485_SERIAL + RS485_SERIAL.begin(baud, SERIAL_8N1); + new taskBuffer(&RS485_SERIAL); +#endif + for (taskBuffer * t=first;t;t=t->next) t->_txPin = txPin; + pinMode(txPin, OUTPUT); + digitalWrite(txPin, LOW); +} + +void taskBuffer::loop() { + for (taskBuffer * t=first;t;t=t->next) t->loop2(); +} + +void taskBuffer::loop2() { + // process received commands here + while (serial->available()) { + char ch = serial->read(); + if (!inCommandPayload) { + if (ch == STARTBYTE) { + inCommandPayload = PAYLOAD_NORMAL; + bufferLength = 0; + buffer[0] = '\0'; + } + } else { // if (inCommandPayload) + if (bufferLength < (COMMAND_BUFFER_SIZE-1)) + buffer[bufferLength++] = ch; + if (inCommandPayload > PAYLOAD_NORMAL) { + if (inCommandPayload > 32 + 2) { // String way too long + ch = ENDBYTE; // we end this nonsense + inCommandPayload = PAYLOAD_NORMAL; + DIAG(F("Parse error: Unbalanced string")); + // fall through to ending parsing below + } else if (ch == '"') { // String end + inCommandPayload = PAYLOAD_NORMAL; + continue; // do not fall through + } else + inCommandPayload++; + } + if (inCommandPayload == PAYLOAD_NORMAL) { + if (ch == ENDBYTE) { + buffer[bufferLength] = '\0'; + parseRx(buffer); + inCommandPayload = PAYLOAD_FALSE; + break; + } else if (ch == '"') { + inCommandPayload = PAYLOAD_STRING; + } + } + } + } +} + +void taskBuffer::parseRx(uint8_t *buf) { + // pass on what we got + bool found = (buf[0] != STARTBYTE); + for (byte *b=buf; b[0] != '\0'; b++) { + if (found) { + parseOne(b); + found=false; + } + if (b[0] == STARTBYTE) + found = true; + } +} + +void taskBuffer::parseOne(uint8_t *buf) { + // finaly, process the darn data + while (buf[0] == '<' || buf[0] == ' ') + buf++; // strip off any number of < or spaces + + uint8_t toNode = buf[0]; + if (toNode != 0) return; // not for master + uint8_t fromNode = buf[1]; + uint8_t opcode = buf[2]; + + RSprotonode *node = RSprotonode::findNode(fromNode); + switch (opcode) { + case EXIOPINS: + {node->_numDigitalPins = buf[3]; + node->_numAnaloguePins = buf[4]; + + // See if we already have suitable buffers assigned + if (node->_numDigitalPins>0) { + size_t digitalBytesNeeded = (node->_numDigitalPins + 7) / 8; + if (node->_digitalPinBytes < digitalBytesNeeded) { + // Not enough space, free any existing buffer and allocate a new one + if (node->_digitalPinBytes > 0) free(node->_digitalInputStates); + if ((node->_digitalInputStates = (byte*) calloc(digitalBytesNeeded, 1)) != NULL) { + node->_digitalPinBytes = digitalBytesNeeded; + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), fromNode, digitalBytesNeeded); + //_deviceState = DEVSTATE_FAILED; + node->_digitalPinBytes = 0; + return; + } + } + } + + if (node->_numAnaloguePins>0) { + size_t analogueBytesNeeded = node->_numAnaloguePins * 2; + if (node->_analoguePinBytes < analogueBytesNeeded) { + // Free any existing buffers and allocate new ones. + if (node->_analoguePinBytes > 0) { + free(node->_analogueInputBuffer); + free(node->_analogueInputStates); + free(node->_analoguePinMap); + } + node->_analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1); + node->_analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1); + node->_analoguePinMap = (uint8_t*) calloc(node->_numAnaloguePins, 1); + if (node->_analogueInputStates != NULL && + node->_analogueInputBuffer != NULL && + node->_analoguePinMap != NULL) { + node->_analoguePinBytes = analogueBytesNeeded; + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), fromNode); + //_deviceState = DEVSTATE_FAILED; + node->_analoguePinBytes = 0; + return; + } + } + } + break;} + case EXIOPINS: { + for (int i = 3; i < node->_numAnaloguePins; i++) { + node->_analoguePinMap[i] = buf[i]; + } + break; + } + case EXIOVER: { + node->_majorVer = buf[3]; + node->_minorVer = buf[4]; + node->_patchVer = buf[5]; + break; + } + case EXIORDY: { + node->resFlag = 1; + break; + } + case EXIOERR: { + node->resFlag = -1; + break; + } + case EXIORDD: { + for (int i = 3; i < (node->_numDigitalPins+7)/8; i++) { + node->_digitalInputStates[i-3] = buf[i]; + } + break; + } + case EXIORDAN: { + for (int i = 3; i < node->_numAnaloguePins*2; i++) { + node->_analogueInputBuffer[i-3] = buf[i]; + } + break; + } + } +} + +/************************************************************ + * RSproto implementation + ************************************************************/ + +// Constructor for RSproto +RSproto::RSproto(HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA) { + _baud = baud; + _serialD = &serial; + _txPin = txPin; + _busNo = 0; + task->init(baud, cycleTimeMS, txPin); + _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 RSproto chain. + _nextBus = _busList; + _busList = this; +} + + + /* -= clearRxBuffer =- // // BLOCKING method to empty stray data in RX buffer @@ -107,111 +306,15 @@ void RSproto::clearRxBuffer() { void RSproto::_loop(unsigned long currentMicros) { _currentMicros = currentMicros; - if (_currentNode == NULL) { - _currentNode = _nodeListStart; + //if (_currentNode == NULL) { + // _currentNode = _nodeListStart; - } + //} - if (_currentMicros - _cycleStartTime < _cycleTime) return; - _cycleStartTime = _currentMicros; - if (_currentNode == NULL) return; - - bool flagOK = true; -#if defined(RSproto_STM_COMM) - ArduinoPins::fastWriteDigital(RSproto_STM_COMM,HIGH); -#endif -if (nodesInitialized()) { - memcpy(_currentNode->_analogueInputStates, _currentNode->_analogueInputBuffer, _currentNode->_analoguePinBytes); // Copy I2C input buffer to states - 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] = _currentNode->getNodeID(); - _currentNode->_readCommandBuffer[1] = 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->write(initBuffer, 1); - _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(); - bool rxDone = false; - byte tmpByte; - len = _serialD->readBytesUntil(0xFE,_currentNode->_digitalInputStates, 25); - if (!true/*crcGood(_currentNode->_digitalInputStates,sizeof(_currentNode->_digitalInputStates)-2)*/) { - DIAG(F("EX-IOExpander485 CRC error on node %d"), _currentNode->getNodeID()); - flagOK = false; - } - if (!testAndStripMasterFlag(_currentNode->_digitalInputStates)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_currentNode->getNodeID()); - 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] = _currentNode->getNodeID(); - _currentNode->_readCommandBuffer[1] = 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->write(initBuffer, 1); - _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(); - bool rxDone = false; - byte tmpByte; - len = _serialD->readBytesUntil(0xFE,_currentNode->_analogueInputBuffer, 25); - if (!true/*crcGood(_currentNode->_digitalInputStates,sizeof(_currentNode->_digitalInputStates)-2)*/) { - DIAG(F("EX-IOExpander485 CRC error on node %d"), _currentNode->getNodeID()); - flagOK = false; - } - if (!testAndStripMasterFlag(_currentNode->_digitalInputStates)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_currentNode->getNodeID()); - if (!waitReceive) _refreshOperation = 0; - _lastAnalogueRead = currentMicros; - _readState = RDS_ANALOGUE; - } - break; - if(flagOK && !waitReceive) _currentNode = _currentNode->getNext(); - } -} -#if defined(RSproto_STM_OK) - if (flagOK == true) { - ArduinoPins::fastWriteDigital(RSproto_STM_OK,HIGH); - } else { - ArduinoPins::fastWriteDigital(RSproto_STM_OK,LOW); - } -#endif -#if defined(RSproto_STM_FAIL) - if (flagOK == false) { - ArduinoPins::fastWriteDigital(RSproto_STM_FAIL,HIGH); - } else { - ArduinoPins::fastWriteDigital(RSproto_STM_FAIL,LOW); - } -#endif -#if defined(RSproto_STM_COMM) - ArduinoPins::fastWriteDigital(RSproto_STM_COMM,LOW); -#endif + //if (_currentMicros - _cycleStartTime < _cycleTime) return; + //_cycleStartTime = _currentMicros; + //if (_currentNode == NULL) return; + task->loop(); } @@ -232,8 +335,8 @@ RSprotonode::RSprotonode(VPIN firstVpin, int nPins, uint8_t nodeID) { _nPins = nPins; _busNo = 0; _nodeID = nodeID; - bus = bus->findBus(0); - _serial = bus->_serialD; + //bus = bus->findBus(0); + //_serial = bus->_serialD; if (_nodeID > 254) _nodeID = 254; // Add this device to HAL device list @@ -253,35 +356,13 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun int pin = vpin - _firstVpin; uint16_t pullup = params[0]; - uint8_t outBuffer[6] = {_nodeID, EXIODPUP, pin, pullup}; - uint8_t responseBuffer[3]; - bus->_busy = true; - bus->updateCrc(outBuffer,4); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - _serial->write(outBuffer, 6); - _serial->write(initBuffer, 1); - _serial->flush(); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); + uint8_t outBuffer[6] = {EXIODPUP, pin, pullup}; + task->doCommand(outBuffer,3); unsigned long startMillis = millis(); - while (!_serial->available()) { - if (millis() - startMillis > 500) return false; - } - uint16_t len = 0; - unsigned long startMicros = micros(); - bool rxDone = false; - byte tmpByte; - len = _serial->readBytesUntil(0xFE,responseBuffer, 25); - bus->_busy = false; - if (true/*bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)*/) { - if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - if (responseBuffer[0] == EXIORDY) { - } else { + while (resFlag == 0 && millis() - startMillis < 500); // blocking for now + if (resFlag != 1) { DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); } - } else { - DIAG(F("EX-IOExpander485 node %d CRC Error"), _nodeID); - } - } int RSprotonode::_configureAnalogIn(VPIN vpin) { @@ -318,84 +399,7 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun } void RSprotonode::_begin() { - //pinMode(bus->_txPin, OUTPUT); - //ArduinoPins::fastWriteDigital(bus->_txPin, LOW); - uint8_t receiveBuffer[5]; - - uint8_t commandBuffer[7] = {_nodeID, EXIOINIT, (uint8_t)_nPins, (_firstVpin & (uint8_t)0xFF), (_firstVpin >> (uint8_t)8)}; - bus->updateCrc(commandBuffer,5); - - //_serial->begin(115200); - //ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - digitalWrite(bus->_txPin,HIGH); - unsigned long startMillis = millis(); - - _serial->write(commandBuffer, 7); - _serial->write(initBuffer, 1); - _serial->flush(); - digitalWrite(bus->_txPin,LOW); - //ArduinoPins::fastWriteDigital(bus->_txPin, LOW); - startMillis = millis(); - while (!_serial->available()) { - if (millis() - startMillis >= 500) return; - } - uint16_t len = 0; - unsigned long startMicros = micros(); - byte tmpByte; - bool rxDone = false; - len = _serial->readBytesUntil(0xFE,receiveBuffer, 25); - DIAG(F("rxcode:%d from node"),receiveBuffer[1]); - if (receiveBuffer[1] == EXIOPINS /*&& bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-3)*/) { - if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - _numDigitalPins = receiveBuffer[1]; - _numAnaloguePins = receiveBuffer[2]; - // See if we already have suitable buffers assigned - if (_numDigitalPins>0) { - size_t digitalBytesNeeded = (_numDigitalPins + 7) / 8; - if (_digitalPinBytes < digitalBytesNeeded) { - // Not enough space, free any existing buffer and allocate a new one - if (_digitalPinBytes > 0) free(_digitalInputStates); - if ((_digitalInputStates = (byte*) calloc(digitalBytesNeeded, 1)) != NULL) { - _digitalPinBytes = digitalBytesNeeded; - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), _nodeID, digitalBytesNeeded); - //_deviceState = DEVSTATE_FAILED; - _digitalPinBytes = 0; - return; - } - } - } - - if (_numAnaloguePins>0) { - size_t analogueBytesNeeded = _numAnaloguePins * 2; - if (_analoguePinBytes < analogueBytesNeeded) { - // Free any existing buffers and allocate new ones. - if (_analoguePinBytes > 0) { - free(_analogueInputBuffer); - free(_analogueInputStates); - free(_analoguePinMap); - } - _analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1); - _analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1); - _analoguePinMap = (uint8_t*) calloc(_numAnaloguePins, 1); - if (_analogueInputStates != NULL && - _analogueInputBuffer != NULL && - _analoguePinMap != NULL) { - _analoguePinBytes = analogueBytesNeeded; - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), _nodeID); - //_deviceState = DEVSTATE_FAILED; - _analoguePinBytes = 0; - return; - } - } - } - } else { - 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] = _nodeID; commandBuffer[1] = EXIOINITA; bus->updateCrc(commandBuffer,2); @@ -416,9 +420,7 @@ void RSprotonode::_begin() { DIAG(F("rxcode:%d from node"),receiveBuffer[1]); if (true/*bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)*/) { if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - for (int i = 0; i < _numAnaloguePins; i++) { - _analoguePinMap[i] = receiveBuffer[i]; - } + } uint8_t versionBuffer[5]; commandBuffer[0] = _nodeID; @@ -441,9 +443,7 @@ void RSprotonode::_begin() { DIAG(F("rxcode:%d.%d.%d from node"),versionBuffer[1],versionBuffer[2],versionBuffer[3]); if (true/*bus->crcGood(versionBuffer,sizeof(versionBuffer)-2)*/) { if (!bus->testAndStripMasterFlag(versionBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - _majorVer = versionBuffer[1]; - _minorVer = versionBuffer[2]; - _patchVer = versionBuffer[3]; + DIAG(F("EX-IOExpander485 device found, node:%d, Version v%d.%d.%d"), _nodeID, _majorVer, _minorVer, _patchVer); } #ifdef DIAG_IO diff --git a/IO_RSproto.h b/IO_RSproto.h index fd3d05aa..1f9aea4a 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -46,6 +46,67 @@ #include "IODevice.h" class RSproto; +class RSprotonode; + +#ifndef COMMAND_BUFFER_SIZE + #define COMMAND_BUFFER_SIZE 100 +#endif +#ifndef RS485_SERIAL + #define RS485_SERIAL Serial1 +#endif +class taskBuffer +{ +private: +static taskBuffer * first; + RSprotonode *node; + Stream * serial; + uint8_t _txPin; + uint8_t _nodeID; + uint8_t _commandType; + byte bufferLength; + byte buffer[COMMAND_BUFFER_SIZE]; + taskBuffer * next; + uint8_t endChar[1] = {0xFE}; + byte inCommandPayload; + // EX-IOExpander protocol flags + enum { + EXIOINIT = 0xE0, // Flag to initialise setup procedure + EXIORDY = 0xE1, // Flag we have completed setup procedure, also for EX-IO to ACK setup + EXIODPUP = 0xE2, // Flag we're sending digital pin pullup configuration + EXIOVER = 0xE3, // Flag to get version + EXIORDAN = 0xE4, // Flag to read an analogue input + EXIOWRD = 0xE5, // Flag for digital write + EXIORDD = 0xE6, // Flag to read digital input + EXIOENAN = 0xE7, // Flag to enable an analogue pin + EXIOINITA = 0xE8, // Flag we're receiving analogue pin mappings + EXIOPINS = 0xE9, // Flag we're receiving pin counts for buffers + EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM) + EXIOERR = 0xEF, // Flag we've received an error + }; + // RSproto protocol frame bytes + enum { + STARTBYTE = 0xFD, + ENDBYTE = 0xFE, + }; + uint16_t _calculateCrc(uint8_t *buf, uint16_t len); + void doCommand2(uint8_t *commandBuffer=NULL, int commandSize=0); + void loop2(); + void parseRx(uint8_t *buf); + void parseOne(uint8_t *buf); +public: + taskBuffer(Stream * myserial); + ~taskBuffer(); + void updateCrc(uint8_t *crcBuf, uint8_t *buf, uint16_t len); + bool crcGood(uint8_t *buf, uint16_t len); + static void doCommand(uint8_t *commandBuffer=NULL, int commandSize=0); + static void init(unsigned long baud, uint16_t cycleTimeMS=500, int8_t txPin=-1); + static void loop(); +}; + + + + + /********************************************************************** * RSprotonode class @@ -62,6 +123,7 @@ class RSprotonode : public IODevice { RSprotonode *_next = NULL; bool _initialised = false; RSproto *bus; + taskBuffer *task; HardwareSerial* _serial; // EX-IOExpander protocol flags enum { @@ -78,7 +140,7 @@ class RSprotonode : public IODevice { EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM) EXIOERR = 0xEF, // Flag we've received an error }; - + static RSprotonode *_nodeList; public: enum ProfileType : int { Instant = 0, // Moves immediately between positions (if duration not specified) @@ -105,7 +167,8 @@ class RSprotonode : public IODevice { 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; - uint8_t initBuffer[1] = {0xFE}; + int resFlag = 0; + bool _initalized; static void create(VPIN firstVpin, int nPins, uint8_t nodeID) { if (checkNoOverlap(firstVpin, nPins)) new RSprotonode(firstVpin, nPins, nodeID); @@ -119,6 +182,13 @@ class RSprotonode : public IODevice { RSprotonode *getNext() { return _next; } + static RSprotonode *findNode(uint8_t nodeID) { + for (RSprotonode *node = _nodeList; node != NULL; node = node->getNext()) { + if (node->getNodeID() == nodeID) + return node; + } + return NULL; + } void setNext(RSprotonode *node) { _next = node; } @@ -170,7 +240,7 @@ class RSproto : public IODevice { private: // Here we define the device-specific variables. uint8_t _busNo; - + taskBuffer *task; unsigned long _cycleStartTime = 0; unsigned long _timeoutStart = 0; unsigned long _cycleTime; // target time between successive read/write cycles, microseconds @@ -196,22 +266,8 @@ class RSproto : public IODevice { const unsigned long _digitalRefresh = 10000UL; // Delay refreshing digital inputs for 10ms const unsigned long _analogueRefresh = 50000UL; // Delay refreshing analogue inputs for 50ms - // EX-IOExpander protocol flags - enum { - EXIOINIT = 0xE0, // Flag to initialise setup procedure - EXIORDY = 0xE1, // Flag we have completed setup procedure, also for EX-IO to ACK setup - EXIODPUP = 0xE2, // Flag we're sending digital pin pullup configuration - EXIOVER = 0xE3, // Flag to get version - EXIORDAN = 0xE4, // Flag to read an analogue input - EXIOWRD = 0xE5, // Flag for digital write - EXIORDD = 0xE6, // Flag to read digital input - EXIOENAN = 0xE7, // Flag to enable an analogue pin - EXIOINITA = 0xE8, // Flag we're receiving analogue pin mappings - EXIOPINS = 0xE9, // Flag we're receiving pin counts for buffers - EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM) - EXIOERR = 0xEF, // Flag we've received an error - }; - uint16_t _calculateCrc(uint8_t *buf, uint16_t len); + + RSprotonode *_nodeListStart = NULL, *_nodeListEnd = NULL; RSprotonode *_currentNode = NULL; @@ -237,7 +293,6 @@ class RSproto : public IODevice { bool _busy = false; unsigned long _baud; int taskCnt = 0; - HardwareSerial *_serialD; uint8_t initBuffer[1] = {0xFE}; bool testAndStripMasterFlag(uint8_t *buf) { if (buf[0] != 0xFD) return false; // why did we not get a master flag? bad node? @@ -246,18 +301,15 @@ class RSproto : public IODevice { } - void updateCrc(uint8_t *buf, uint16_t len); - bool crcGood(uint8_t *buf, uint16_t len); + void clearRxBuffer(); - static void create(HardwareSerial& serial, unsigned long baud, uint16_t cycleTimeMS=500, int8_t txPin=-1, int waitA=10) { - new RSproto(serial, baud, cycleTimeMS, txPin, waitA); + static void create(unsigned long baud, uint16_t cycleTimeMS=500, int8_t txPin=-1, int waitA=10) { + new RSproto(baud, cycleTimeMS, txPin, waitA); } // Device-specific initialisation void _begin() override { - _serialD->begin(_baud, SERIAL_8N1); - _serialD->flush(); #if defined(RSproto_STM_OK) pinMode(RSproto_STM_OK, OUTPUT); ArduinoPins::fastWriteDigital(RSproto_STM_OK,LOW); @@ -315,7 +367,7 @@ class RSproto : public IODevice { } protected: - RSproto(HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA); + RSproto(unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA); public: From 0a6bc136b07342ad0bc28cc1a9071b5313ea1205 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sun, 15 Dec 2024 16:36:02 -0500 Subject: [PATCH 52/73] compiles! --- IO_RSproto.cpp | 253 +++++++++++++++---------------------------------- IO_RSproto.h | 35 +++---- 2 files changed, 89 insertions(+), 199 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index 6779438e..639a0275 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -25,12 +25,15 @@ static const byte PAYLOAD_FALSE = 0; static const byte PAYLOAD_NORMAL = 1; static const byte PAYLOAD_STRING = 2; +taskBuffer * taskBuffer::first=NULL; + taskBuffer::taskBuffer(Stream * myserial) { - // constructor + serial = myserial; next=first; first=this; - serial = myserial; + bufferLength=0; + inCommandPayload=PAYLOAD_FALSE; } taskBuffer::~taskBuffer() @@ -81,7 +84,7 @@ uint16_t taskBuffer::_calculateCrc(uint8_t *buf, uint16_t len) { } void taskBuffer::doCommand(uint8_t *commandBuffer, int commandSize) { - for (taskBuffer * t=first;t;t=t->next) t->doCommand2(nodeID,commandBuffer,commandSize); + for (taskBuffer * t=first;t;t=t->next) t->doCommand2(commandBuffer,commandSize); } void taskBuffer::doCommand2(uint8_t *commandBuffer, int commandSize) { @@ -100,7 +103,7 @@ void taskBuffer::doCommand2(uint8_t *commandBuffer, int commandSize) { digitalWrite(_txPin,LOW); } -void taskBuffer::init(unsigned long baud, uint16_t cycleTimeMS, int8_t txPin) { +void taskBuffer::init(unsigned long baud, int8_t txPin) { #ifdef RS485_SERIAL RS485_SERIAL.begin(baud, SERIAL_8N1); new taskBuffer(&RS485_SERIAL); @@ -175,8 +178,8 @@ void taskBuffer::parseOne(uint8_t *buf) { if (toNode != 0) return; // not for master uint8_t fromNode = buf[1]; uint8_t opcode = buf[2]; - - RSprotonode *node = RSprotonode::findNode(fromNode); + RSproto *bus = RSproto::findBus(0); + RSprotonode *node = bus->findNode(fromNode); switch (opcode) { case EXIOPINS: {node->_numDigitalPins = buf[3]; @@ -223,17 +226,20 @@ void taskBuffer::parseOne(uint8_t *buf) { } } } + node->resFlag = 1; break;} - case EXIOPINS: { + case EXIOINITA: { for (int i = 3; i < node->_numAnaloguePins; i++) { node->_analoguePinMap[i] = buf[i]; } + node->resFlag = 1; break; } case EXIOVER: { node->_majorVer = buf[3]; node->_minorVer = buf[4]; node->_patchVer = buf[5]; + node->resFlag = 1; break; } case EXIORDY: { @@ -248,12 +254,14 @@ void taskBuffer::parseOne(uint8_t *buf) { for (int i = 3; i < (node->_numDigitalPins+7)/8; i++) { node->_digitalInputStates[i-3] = buf[i]; } + node->resFlag = 1; break; } case EXIORDAN: { for (int i = 3; i < node->_numAnaloguePins*2; i++) { node->_analogueInputBuffer[i-3] = buf[i]; } + node->resFlag = 1; break; } } @@ -264,14 +272,11 @@ void taskBuffer::parseOne(uint8_t *buf) { ************************************************************/ // Constructor for RSproto -RSproto::RSproto(HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA) { +RSproto::RSproto(unsigned long baud, int8_t txPin) { _baud = baud; - _serialD = &serial; _txPin = txPin; _busNo = 0; - task->init(baud, cycleTimeMS, txPin); - _cycleTime = cycleTimeMS * 1000UL; // convert from milliseconds to microseconds. - _waitA = waitA; + task->init(baud, txPin); if (_waitA < 3) _waitA = 3; // Add device to HAL device chain IODevice::addDevice(this); @@ -281,22 +286,6 @@ RSproto::RSproto(HardwareSerial &serial, unsigned long baud, uint16_t cycleTimeM _busList = this; } - - -/* -= clearRxBuffer =- -// -// BLOCKING method to empty stray data in RX buffer -*/ -void RSproto::clearRxBuffer() { - unsigned long startMicros = micros(); - do { - if (_serialD->available() > 0) { - startMicros = micros(); - _serialD->read(); - } - } while (micros() - startMicros < _frameTimeout || !_serialD->available()); -} - /* -= _loop =- // // Main loop function for RSproto. @@ -357,97 +346,58 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun uint16_t pullup = params[0]; uint8_t outBuffer[6] = {EXIODPUP, pin, pullup}; - task->doCommand(outBuffer,3); unsigned long startMillis = millis(); + task->doCommand(outBuffer,3); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now - if (resFlag != 1) { - DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); - } + if (resFlag != 1) { + DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); + } + resFlag = 0; } int RSprotonode::_configureAnalogIn(VPIN vpin) { int pin = vpin - _firstVpin; //RSproto *mainrs = RSproto::findBus(_busNo); - uint8_t commandBuffer[5] = {(uint8_t) _nodeID, EXIOENAN, (uint8_t) pin}; - uint8_t responseBuffer[3]; - bus->_busy = true; - bus->updateCrc(commandBuffer,3); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - _serial->write(commandBuffer, 5); - _serial->write(initBuffer, 1); - _serial->flush(); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); + uint8_t commandBuffer[5] = {EXIOENAN, (uint8_t) pin}; unsigned long startMillis = millis(); - while (!_serial->available()) { - if (millis() - startMillis > 500) return 0; - } - uint16_t len = 0; - unsigned long startMicros = micros(); - bool rxDone = false; - byte tmpByte; - len = _serial->readBytesUntil(0xFE,responseBuffer, 25); - bus->_busy = false; - if (true/*bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)*/) { - if (!bus->testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - if (responseBuffer[0] != EXIORDY) { - DIAG(F("EX-IOExpander485: Vpin %u on node %d cannot be used as an analogue input pin"), (int) pin, (int) _nodeID); - } - } else { - DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) _nodeID); + task->doCommand(commandBuffer, 2); + while (resFlag == 0 && millis() - startMillis < 500); // blocking for now + if (resFlag != 1) { + DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); } + resFlag = 0; return false; } void RSprotonode::_begin() { + uint8_t commandBuffer[4] = {EXIOINIT, (uint8_t)_nPins, (uint8_t)(_firstVpin & 0xFF), (uint8_t)(_firstVpin >> 8)}; + unsigned long startMillis = millis(); + task->doCommand(commandBuffer,4); + while (resFlag == 0 && millis() - startMillis < 500); // blocking for now + if (resFlag != 1) { + DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOINIT"), _nodeID); + } + resFlag = 0; + commandBuffer[0] = EXIOINITA; + startMillis = millis(); + task->doCommand(commandBuffer,1); + while (resFlag == 0 && millis() - startMillis < 500); // blocking for now + if (resFlag != 1) { + DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOINITA"), _nodeID); + } + resFlag = 0; + commandBuffer[0] = EXIOVER; + startMillis = millis(); + task->doCommand(commandBuffer,1); + while (resFlag == 0 && millis() - startMillis < 500); // blocking for now + if (resFlag != 1) { + DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); + } else DIAG(F("EX-IOExpander device found, Node:%d, Version v%d.%d.%d"), _nodeID, _majorVer, _minorVer, _patchVer); + resFlag = 0; + - commandBuffer[0] = _nodeID; - commandBuffer[1] = EXIOINITA; - bus->updateCrc(commandBuffer,2); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - _serial->write(commandBuffer, 4); - _serial->write(initBuffer, 1); - _serial->flush(); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); - startMillis = millis(); - while (!_serial->available()) { - if (millis() - startMillis >= 500) return; - } - len = 0; - startMicros = micros(); - rxDone = false; - len = _serial->readBytesUntil(0xFE,receiveBuffer, 25); - - DIAG(F("rxcode:%d from node"),receiveBuffer[1]); - if (true/*bus->crcGood(receiveBuffer,sizeof(receiveBuffer)-2)*/) { - if (!bus->testAndStripMasterFlag(receiveBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - - } - uint8_t versionBuffer[5]; - commandBuffer[0] = _nodeID; - commandBuffer[1] = EXIOVER; - bus->updateCrc(commandBuffer,2); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - _serial->write(commandBuffer, 4); - _serial->write(initBuffer, 1); - _serial->flush(); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); - startMillis = millis(); - while (!_serial->available()) { - if (millis() - startMillis >= 500) return; - } - len = 0; - startMicros = micros(); - rxDone = false; - len = _serial->readBytesUntil(0xFE,receiveBuffer, 25); - - DIAG(F("rxcode:%d.%d.%d from node"),versionBuffer[1],versionBuffer[2],versionBuffer[3]); - if (true/*bus->crcGood(versionBuffer,sizeof(versionBuffer)-2)*/) { - if (!bus->testAndStripMasterFlag(versionBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - - DIAG(F("EX-IOExpander485 device found, node:%d, Version v%d.%d.%d"), _nodeID, _majorVer, _minorVer, _patchVer); - } #ifdef DIAG_IO - _display(); + _display(); #endif } @@ -462,43 +412,18 @@ void RSprotonode::_write(VPIN vpin, int value) { if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; uint8_t digitalOutBuffer[6]; - uint8_t responseBuffer[3]; - digitalOutBuffer[0] = (uint8_t) _nodeID; - digitalOutBuffer[1] = EXIOWRD; - digitalOutBuffer[2] = (uint8_t) pin; - digitalOutBuffer[3] = (uint8_t) value; - bus->_busy = true; - bus->updateCrc(digitalOutBuffer,4); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - _serial->write(digitalOutBuffer, 6); - _serial->write(initBuffer, 1); - _serial->flush(); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); - unsigned long startMillis = millis(); - while (!_serial->available()) { - if (millis() - startMillis >= 500) return; - } - uint16_t len = 0; - unsigned long startMicros = micros(); - bool rxDone = false; - byte tmpByte; - len = _serial->readBytesUntil(0xFE,responseBuffer, 25); - bus->_busy = false; - if (true/*bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)*/) { - if (!testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - if (responseBuffer[0] != EXIORDY) { - DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital output pin"), pin); - } - } else { - DIAG(F("EX-IOExpander485 node %d CRC Error"), _nodeID); - } + digitalOutBuffer[1] = EXIOWRD; + digitalOutBuffer[2] = (uint8_t) pin; + digitalOutBuffer[3] = (uint8_t) value; + unsigned long startMillis = millis(); + task->doCommand(digitalOutBuffer,3); + while (resFlag == 0 && millis() - startMillis < 500); // blocking for now + if (resFlag != 1) { + DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); + } + resFlag = 0; } - bool RSprotonode::testAndStripMasterFlag(uint8_t *buf) { - if (buf[0] != 0xFF) return false; // why did we not get a master flag? bad node? - for (int i = 0; i < sizeof(buf)-1; i++) buf[i] = buf[i+1]; // shift array to begining - return true; - } int RSprotonode::_readAnalogue(VPIN vpin) { if (_deviceState == DEVSTATE_FAILED) return 0; int pin = vpin - _firstVpin; @@ -515,41 +440,19 @@ void RSprotonode::_write(VPIN vpin, int value) { void RSprotonode::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) { uint8_t servoBuffer[7]; uint8_t responseBuffer[1]; - - if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; - servoBuffer[0] = (uint8_t) _nodeID; - servoBuffer[1] = EXIOWRAN; - servoBuffer[2] = (uint8_t) pin; - servoBuffer[3] = (uint8_t) value & 0xFF; - servoBuffer[4] = (uint8_t) value >> 8; - servoBuffer[5] = (uint8_t) profile; - servoBuffer[6] = (uint8_t) duration & 0xFF; - servoBuffer[7] = (uint8_t) duration >> 8; - bus->_busy = true; - bus->updateCrc(servoBuffer,8); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - _serial->write(servoBuffer, 10); - _serial->write(initBuffer, 1); - _serial->flush(); - if (bus->_txPin != VPIN_NONE) ArduinoPins::fastWriteDigital(bus->_txPin, LOW); - unsigned long startMillis = millis(); - while (!_serial->available()) { - if (millis() - startMillis >= 500) return; - } - uint16_t len = 0; - unsigned long startMicros = micros(); - bool rxDone = false; - byte tmpByte; - len = _serial->readBytesUntil(0xFE,responseBuffer, 25); - bus->_busy = false; - if (!true/*bus->crcGood(responseBuffer,sizeof(responseBuffer)-2)*/) { - DIAG(F("EX-IOExpander485 node %d CRC Error"), (int) _nodeID); - //_deviceState = DEVSTATE_FAILED; - } else { - if (!bus->testAndStripMasterFlag(responseBuffer)) DIAG(F("Foreign RSproto Device! no master flag from node %d"),_nodeID); - if (responseBuffer[0] != EXIORDY) { - DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a servo/PWM pin"), pin); - } - } + servoBuffer[0] = EXIOWRAN; + servoBuffer[1] = (uint8_t) pin; + servoBuffer[2] = (uint8_t) value & 0xFF; + servoBuffer[3] = (uint8_t) value >> 8; + servoBuffer[4] = (uint8_t) profile; + servoBuffer[5] = (uint8_t) duration & 0xFF; + servoBuffer[6] = (uint8_t) duration >> 8; + unsigned long startMillis = millis(); + task->doCommand(servoBuffer,7); + while (resFlag == 0 && millis() - startMillis < 500); // blocking for now + if (resFlag != 1) { + DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); + } + resFlag = 0; } \ No newline at end of file diff --git a/IO_RSproto.h b/IO_RSproto.h index 1f9aea4a..d39a6152 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -57,7 +57,7 @@ class RSprotonode; class taskBuffer { private: -static taskBuffer * first; +static taskBuffer *first; RSprotonode *node; Stream * serial; uint8_t _txPin; @@ -65,7 +65,7 @@ static taskBuffer * first; uint8_t _commandType; byte bufferLength; byte buffer[COMMAND_BUFFER_SIZE]; - taskBuffer * next; + taskBuffer *next; uint8_t endChar[1] = {0xFE}; byte inCommandPayload; // EX-IOExpander protocol flags @@ -99,7 +99,7 @@ static taskBuffer * first; void updateCrc(uint8_t *crcBuf, uint8_t *buf, uint16_t len); bool crcGood(uint8_t *buf, uint16_t len); static void doCommand(uint8_t *commandBuffer=NULL, int commandSize=0); - static void init(unsigned long baud, uint16_t cycleTimeMS=500, int8_t txPin=-1); + static void init(unsigned long baud, int8_t txPin=-1); static void loop(); }; @@ -140,8 +140,9 @@ class RSprotonode : public IODevice { EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM) EXIOERR = 0xEF, // Flag we've received an error }; - static RSprotonode *_nodeList; + public: + static RSprotonode *_nodeList; enum ProfileType : int { Instant = 0, // Moves immediately between positions (if duration not specified) UseDuration = 0, // Use specified duration @@ -182,13 +183,7 @@ class RSprotonode : public IODevice { RSprotonode *getNext() { return _next; } - static RSprotonode *findNode(uint8_t nodeID) { - for (RSprotonode *node = _nodeList; node != NULL; node = node->getNext()) { - if (node->getNodeID() == nodeID) - return node; - } - return NULL; - } + void setNext(RSprotonode *node) { _next = node; } @@ -285,6 +280,10 @@ class RSproto : public IODevice { } public: + static void create(unsigned long baud, int8_t txPin=-1) { + new RSproto(baud, txPin); + } + int _CommMode = 0; int _opperation = 0; uint16_t _pullup; @@ -294,19 +293,7 @@ class RSproto : public IODevice { unsigned long _baud; int taskCnt = 0; uint8_t initBuffer[1] = {0xFE}; - bool testAndStripMasterFlag(uint8_t *buf) { - if (buf[0] != 0xFD) return false; // why did we not get a master flag? bad node? - for (int i = 0; i < sizeof(buf)-1; i++) buf[i] = buf[i+1]; // shift array to begining - return true; - } - - - void clearRxBuffer(); - static void create(unsigned long baud, uint16_t cycleTimeMS=500, int8_t txPin=-1, int waitA=10) { - new RSproto(baud, cycleTimeMS, txPin, waitA); - } - // Device-specific initialisation void _begin() override { @@ -367,7 +354,7 @@ class RSproto : public IODevice { } protected: - RSproto(unsigned long baud, uint16_t cycleTimeMS, int8_t txPin, int waitA); + RSproto(unsigned long baud, int8_t txPin); public: From 74bfb0ca6eda6ea81c85db5428296845ed53d77e Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sun, 15 Dec 2024 16:43:26 -0500 Subject: [PATCH 53/73] ready for hardware debugging --- IO_RSproto.cpp | 49 ++----------------------------------------------- IO_RSproto.h | 21 --------------------- 2 files changed, 2 insertions(+), 68 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index 639a0275..c150101a 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -41,48 +41,6 @@ taskBuffer::~taskBuffer() // destructor } -/* -= updateCrc =- -// -// add the CRC value from _calculateCrc (2 bytes) to the buffer. -*/ -void taskBuffer::updateCrc(uint8_t *crcBuf, uint8_t *buf, uint16_t len) { - if (sizeof(crcBuf) != 2) return; - uint16_t crc = _calculateCrc(buf, len); - crcBuf[0] = lowByte(crc); - crcBuf[1] = highByte(crc); -} - -/* -= crcGood =- -// -// return TRUE if CRC matched between buffer copy, and calculated. -*/ -bool taskBuffer::crcGood(uint8_t *buf, uint16_t len) { - uint16_t aduCrc = buf[len] | (buf[len + 1] << 8); - uint16_t calculatedCrc = _calculateCrc(buf, len); -#if defined(IO_DIAG) - DIAG(F("CRC is %d Expected %d"),calculatedCrc, aduCrc); -#endif - if (aduCrc == calculatedCrc) return true; - else return false; -} - -/* -= calculateCrc =- -// -// use bitwise XOR to calculate CRC into a 16-bit byte -*/ -uint16_t taskBuffer::_calculateCrc(uint8_t *buf, uint16_t len) { - uint16_t value = 0xFFFF; - for (uint16_t i = 0; i < len; i++) { - value ^= (uint16_t)buf[i]; - for (uint8_t j = 0; j < 8; j++) { - bool lsb = value & 1; - value >>= 1; - if (lsb == true) value ^= 0xA001; - } - } - return value; -} - void taskBuffer::doCommand(uint8_t *commandBuffer, int commandSize) { for (taskBuffer * t=first;t;t=t->next) t->doCommand2(commandBuffer,commandSize); } @@ -90,13 +48,10 @@ void taskBuffer::doCommand(uint8_t *commandBuffer, int commandSize) { void taskBuffer::doCommand2(uint8_t *commandBuffer, int commandSize) { // process commands here to be sent uint8_t crcBuffer[2]; - updateCrc(crcBuffer, commandBuffer, commandSize); - + //_serial->begin(115200); //ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); digitalWrite(_txPin,HIGH); - unsigned long startMillis = millis(); - serial->write(commandBuffer, 7); serial->write(endChar, 1); serial->flush(); @@ -353,6 +308,7 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); } resFlag = 0; + return false; } int RSprotonode::_configureAnalogIn(VPIN vpin) { @@ -439,7 +395,6 @@ void RSprotonode::_write(VPIN vpin, int value) { void RSprotonode::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) { uint8_t servoBuffer[7]; - uint8_t responseBuffer[1]; int pin = vpin - _firstVpin; servoBuffer[0] = EXIOWRAN; servoBuffer[1] = (uint8_t) pin; diff --git a/IO_RSproto.h b/IO_RSproto.h index d39a6152..9a8ece61 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -88,7 +88,6 @@ static taskBuffer *first; STARTBYTE = 0xFD, ENDBYTE = 0xFE, }; - uint16_t _calculateCrc(uint8_t *buf, uint16_t len); void doCommand2(uint8_t *commandBuffer=NULL, int commandSize=0); void loop2(); void parseRx(uint8_t *buf); @@ -96,8 +95,6 @@ static taskBuffer *first; public: taskBuffer(Stream * myserial); ~taskBuffer(); - void updateCrc(uint8_t *crcBuf, uint8_t *buf, uint16_t len); - bool crcGood(uint8_t *buf, uint16_t len); static void doCommand(uint8_t *commandBuffer=NULL, int commandSize=0); static void init(unsigned long baud, int8_t txPin=-1); static void loop(); @@ -155,21 +152,17 @@ class RSprotonode : public IODevice { uint8_t _numDigitalPins = 0; uint8_t _numAnaloguePins = 0; - uint8_t _majorVer = 0; uint8_t _minorVer = 0; uint8_t _patchVer = 0; - uint8_t* _digitalInputStates = NULL; uint8_t* _analogueInputStates = NULL; uint8_t* _analogueInputBuffer = NULL; // buffer for I2C input transfers 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; int resFlag = 0; - bool _initalized; static void create(VPIN firstVpin, int nPins, uint8_t nodeID) { if (checkNoOverlap(firstVpin, nPins)) new RSprotonode(firstVpin, nPins, nodeID); @@ -195,21 +188,11 @@ class RSprotonode : public IODevice { } bool _configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) override; - int _configureAnalogIn(VPIN vpin) override; - void _begin() override; - - int _read(VPIN vpin) override; - - void _write(VPIN vpin, int value) override; - - bool testAndStripMasterFlag(uint8_t *buf); - int _readAnalogue(VPIN vpin) override; - void _writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) override; uint8_t getBusNumber() { @@ -220,7 +203,6 @@ class RSprotonode : public IODevice { 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("")); } - }; /********************************************************************** @@ -266,11 +248,8 @@ class RSproto : public IODevice { RSprotonode *_nodeListStart = NULL, *_nodeListEnd = NULL; RSprotonode *_currentNode = NULL; - uint8_t _exceptionResponse = 0; - uint8_t getExceptionResponse(); uint16_t _receiveDataIndex = 0; // Index of next data byte to be received. RSproto *_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) { From e97830542e92dbecde9b19f8d4e93d191c76d023 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Mon, 16 Dec 2024 06:14:59 -0500 Subject: [PATCH 54/73] change to full-duplex token ring style --- IO_RSproto.cpp | 34 ++++++++++----------- IO_RSproto.h | 80 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 68 insertions(+), 46 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index c150101a..edfd6668 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -1,6 +1,6 @@ /* * © 2024, Travis Farmer. All rights reserved. - * © 2024, Chris Bulliner. All rights reserved. https://github.com/CMB27 + * © 2021 Chris Harlow * * This file is part of DCC++EX API * @@ -47,25 +47,22 @@ void taskBuffer::doCommand(uint8_t *commandBuffer, int commandSize) { void taskBuffer::doCommand2(uint8_t *commandBuffer, int commandSize) { // process commands here to be sent - uint8_t crcBuffer[2]; //_serial->begin(115200); //ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - digitalWrite(_txPin,HIGH); + if (_txPin != -1) digitalWrite(_txPin,HIGH); serial->write(commandBuffer, 7); serial->write(endChar, 1); serial->flush(); - digitalWrite(_txPin,LOW); + if (_txPin != -1) digitalWrite(_txPin,LOW); } -void taskBuffer::init(unsigned long baud, int8_t txPin) { -#ifdef RS485_SERIAL - RS485_SERIAL.begin(baud, SERIAL_8N1); - new taskBuffer(&RS485_SERIAL); -#endif +void taskBuffer::init(HardwareSerial &hwSerial, unsigned long baud, int8_t txPin) { + hwSerial.begin(baud, SERIAL_8N1); + new taskBuffer(&hwSerial); for (taskBuffer * t=first;t;t=t->next) t->_txPin = txPin; - pinMode(txPin, OUTPUT); - digitalWrite(txPin, LOW); + if (txPin != -1) pinMode(txPin, OUTPUT); + if (txPin != -1) digitalWrite(txPin, LOW); } void taskBuffer::loop() { @@ -132,6 +129,7 @@ void taskBuffer::parseOne(uint8_t *buf) { uint8_t toNode = buf[0]; if (toNode != 0) return; // not for master uint8_t fromNode = buf[1]; + if (fromNode == 0) return; // why did out own data come round the ring back to us? uint8_t opcode = buf[2]; RSproto *bus = RSproto::findBus(0); RSprotonode *node = bus->findNode(fromNode); @@ -227,11 +225,12 @@ void taskBuffer::parseOne(uint8_t *buf) { ************************************************************/ // Constructor for RSproto -RSproto::RSproto(unsigned long baud, int8_t txPin) { +RSproto::RSproto(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin) { + _serial = &serial; _baud = baud; _txPin = txPin; - _busNo = 0; - task->init(baud, txPin); + _busNo = busNo; + task->init(serial, baud, txPin); if (_waitA < 3) _waitA = 3; // Add device to HAL device chain IODevice::addDevice(this); @@ -281,7 +280,8 @@ RSprotonode::RSprotonode(VPIN firstVpin, int nPins, uint8_t nodeID) { _nodeID = nodeID; //bus = bus->findBus(0); //_serial = bus->_serialD; - if (_nodeID > 254) _nodeID = 254; + if (_nodeID > 252) _nodeID = 252; // cannot have a node with the frame flags + if (_nodeID < 1) _nodeID = 1; // cannot have a node with the master ID // Add this device to HAL device list IODevice::addDevice(this); @@ -299,8 +299,8 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun if (paramCount != 1) return false; int pin = vpin - _firstVpin; - uint16_t pullup = params[0]; - uint8_t outBuffer[6] = {EXIODPUP, pin, pullup}; + uint8_t pullup = (uint8_t)params[0]; + uint8_t outBuffer[6] = {EXIODPUP, (uint8_t)pin, pullup}; unsigned long startMillis = millis(); task->doCommand(outBuffer,3); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now diff --git a/IO_RSproto.h b/IO_RSproto.h index 9a8ece61..a5640681 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -1,33 +1,35 @@ /* - * © 2024, Travis Farmer. All rights reserved. - * © 2024, Chris Bulliner. All rights reserved. https://github.com/CMB27 - * - * This file is part of DCC++EX API - * - * This is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * It is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with CommandStation. If not, see . - */ +* © 2024, Travis Farmer. All rights reserved. +* © 2021 Chris Harlow +* +* This file is part of DCC++EX API +* +* This is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* It is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with CommandStation. If not, see . +*/ /* * RSproto * ======= * To define a RSproto, example syntax: - * RSproto::create(serial, baud[, cycletime[, pin]]); + * RSproto::create(busNo, serial, baud[, pin]); * + * busNo = the Bus no of the instance. should = 0, unless more than one bus configured for some reason. * 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 RSproto module's DE and !RE terminals for half-duplex operation (default VPIN_NONE) + * pin = pin number connected to RSproto module's DE and !RE terminals for half-duplex operation (default -1) + * if omitted (default), hardware MUST support full-duplex opperation! * * * RSprotoNode @@ -37,7 +39,7 @@ * * firstVPIN = first vpin in block allocated to this device * numVPINs = number of vpins - * nodeID = 0-254 + * nodeID = 1-252 */ #ifndef IO_RS485_H @@ -48,12 +50,32 @@ class RSproto; class RSprotonode; + + #ifndef COMMAND_BUFFER_SIZE #define COMMAND_BUFFER_SIZE 100 #endif -#ifndef RS485_SERIAL - #define RS485_SERIAL Serial1 -#endif + +/********************************************************************** + * taskBuffer class + * + * this stores the task list, and processes the data within it for + * sending. it also handles the incomming data responce. + * Data Frame: + * 0xFD : toNode : fromNode : ~data packet~ : 0xFE + * Start: TO : FROM : DATA : End + * + * Data frame must always start with the Start byte, follow with the + * destination (toNode), follow with the Source (fromNode), contain + * the data packet, and follow with the End byte. + * + * + * Data Packet: + * Command Byte : ~Command Params~ + * + * Data Packet must always precede the parameters with the Command byte. + * this way the data is processed by the correct routine. + **********************************************************************/ class taskBuffer { private: @@ -96,7 +118,7 @@ static taskBuffer *first; taskBuffer(Stream * myserial); ~taskBuffer(); static void doCommand(uint8_t *commandBuffer=NULL, int commandSize=0); - static void init(unsigned long baud, int8_t txPin=-1); + static void init(HardwareSerial &hwSerial, unsigned long baud, int8_t txPin=-1); static void loop(); }; @@ -259,10 +281,10 @@ class RSproto : public IODevice { } public: - static void create(unsigned long baud, int8_t txPin=-1) { - new RSproto(baud, txPin); + static void create(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin=-1) { + new RSproto(busNo, serial, baud, txPin); } - + HardwareSerial* _serial; int _CommMode = 0; int _opperation = 0; uint16_t _pullup; @@ -333,7 +355,7 @@ class RSproto : public IODevice { } protected: - RSproto(unsigned long baud, int8_t txPin); + RSproto(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin); public: From 8532517c4e7f5dcf62708aceb1985873bc09bd78 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Mon, 16 Dec 2024 10:21:47 -0500 Subject: [PATCH 55/73] fixed return states --- IO_RSproto.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index edfd6668..8c86595e 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -306,9 +306,10 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); + return false; } resFlag = 0; - return false; + return true; } int RSprotonode::_configureAnalogIn(VPIN vpin) { @@ -320,9 +321,10 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); + return false; } resFlag = 0; - return false; + return true; } void RSprotonode::_begin() { From 943eaa0484a20070673d79015c86d1ffe64deeb9 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Mon, 16 Dec 2024 12:33:44 -0500 Subject: [PATCH 56/73] made serial writes one operation --- IO_RSproto.cpp | 73 ++++++++++++++++++++++++++++++-------------------- IO_RSproto.h | 1 + 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index 8c86595e..39020619 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -52,8 +52,7 @@ void taskBuffer::doCommand2(uint8_t *commandBuffer, int commandSize) { //ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); if (_txPin != -1) digitalWrite(_txPin,HIGH); serial->write(commandBuffer, 7); - serial->write(endChar, 1); - serial->flush(); + //serial->flush(); if (_txPin != -1) digitalWrite(_txPin,LOW); } @@ -300,9 +299,9 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun int pin = vpin - _firstVpin; uint8_t pullup = (uint8_t)params[0]; - uint8_t outBuffer[6] = {EXIODPUP, (uint8_t)pin, pullup}; + uint8_t outBuffer[] = {0xFD, _nodeID, 0, EXIODPUP, (uint8_t)pin, pullup, 0xFE}; unsigned long startMillis = millis(); - task->doCommand(outBuffer,3); + task->doCommand(outBuffer,7); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); @@ -315,9 +314,9 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun int RSprotonode::_configureAnalogIn(VPIN vpin) { int pin = vpin - _firstVpin; //RSproto *mainrs = RSproto::findBus(_busNo); - uint8_t commandBuffer[5] = {EXIOENAN, (uint8_t) pin}; + uint8_t commandBuffer[] = {0xFD, _nodeID, 0, EXIOENAN, (uint8_t) pin, 0xFE}; unsigned long startMillis = millis(); - task->doCommand(commandBuffer, 2); + task->doCommand(commandBuffer, 6); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); @@ -328,26 +327,34 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun } void RSprotonode::_begin() { - uint8_t commandBuffer[4] = {EXIOINIT, (uint8_t)_nPins, (uint8_t)(_firstVpin & 0xFF), (uint8_t)(_firstVpin >> 8)}; + uint8_t commandBuffer[] = {0xFD, _nodeID, 0, EXIOINIT, (uint8_t)_nPins, (uint8_t)(_firstVpin & 0xFF), (uint8_t)(_firstVpin >> 8), 0xFE}; unsigned long startMillis = millis(); - task->doCommand(commandBuffer,4); - while (resFlag == 0 && millis() - startMillis < 500); // blocking for now + task->doCommand(commandBuffer,8); + while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOINIT"), _nodeID); } resFlag = 0; - commandBuffer[0] = EXIOINITA; + commandBuffer[0] = 0xFD; + commandBuffer[1] = _nodeID; + commandBuffer[2] = 0; + commandBuffer[3] = EXIOINITA; + commandBuffer[4] = 0xFE; startMillis = millis(); - task->doCommand(commandBuffer,1); - while (resFlag == 0 && millis() - startMillis < 500); // blocking for now + task->doCommand(commandBuffer,5); + while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOINITA"), _nodeID); } resFlag = 0; - commandBuffer[0] = EXIOVER; + commandBuffer[0] = 0xFD; + commandBuffer[1] = _nodeID; + commandBuffer[2] = 0; + commandBuffer[3] = EXIOVER; + commandBuffer[4] = 0xFE; startMillis = millis(); - task->doCommand(commandBuffer,1); - while (resFlag == 0 && millis() - startMillis < 500); // blocking for now + task->doCommand(commandBuffer,3); + while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); } else DIAG(F("EX-IOExpander device found, Node:%d, Version v%d.%d.%d"), _nodeID, _majorVer, _minorVer, _patchVer); @@ -369,12 +376,16 @@ int RSprotonode::_read(VPIN vpin) { void RSprotonode::_write(VPIN vpin, int value) { if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; - uint8_t digitalOutBuffer[6]; - digitalOutBuffer[1] = EXIOWRD; - digitalOutBuffer[2] = (uint8_t) pin; - digitalOutBuffer[3] = (uint8_t) value; + uint8_t digitalOutBuffer[7]; + digitalOutBuffer[0] = 0xFD; + digitalOutBuffer[1] = _nodeID; + digitalOutBuffer[2] = 0; + digitalOutBuffer[3] = EXIOWRD; + digitalOutBuffer[4] = (uint8_t) pin; + digitalOutBuffer[5] = (uint8_t) value; + digitalOutBuffer[6] = 0xFE; unsigned long startMillis = millis(); - task->doCommand(digitalOutBuffer,3); + task->doCommand(digitalOutBuffer,7); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); @@ -396,17 +407,21 @@ void RSprotonode::_write(VPIN vpin, int value) { } void RSprotonode::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) { - uint8_t servoBuffer[7]; + uint8_t servoBuffer[11]; int pin = vpin - _firstVpin; - servoBuffer[0] = EXIOWRAN; - servoBuffer[1] = (uint8_t) pin; - servoBuffer[2] = (uint8_t) value & 0xFF; - servoBuffer[3] = (uint8_t) value >> 8; - servoBuffer[4] = (uint8_t) profile; - servoBuffer[5] = (uint8_t) duration & 0xFF; - servoBuffer[6] = (uint8_t) duration >> 8; + servoBuffer[0] = 0xFD; + servoBuffer[1] = _nodeID; + servoBuffer[2] = 0; + servoBuffer[3] = EXIOWRAN; + servoBuffer[4] = (uint8_t) pin; + servoBuffer[5] = (uint8_t) value & 0xFF; + servoBuffer[6] = (uint8_t) value >> 8; + servoBuffer[7] = (uint8_t) profile; + servoBuffer[8] = (uint8_t) duration & 0xFF; + servoBuffer[9] = (uint8_t) duration >> 8; + servoBuffer[10] = 0xFE; unsigned long startMillis = millis(); - task->doCommand(servoBuffer,7); + task->doCommand(servoBuffer,11); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); diff --git a/IO_RSproto.h b/IO_RSproto.h index a5640681..4c67eddf 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -88,6 +88,7 @@ static taskBuffer *first; byte bufferLength; byte buffer[COMMAND_BUFFER_SIZE]; taskBuffer *next; + uint8_t startChar[1] = {0xFD}; uint8_t endChar[1] = {0xFE}; byte inCommandPayload; // EX-IOExpander protocol flags From f0b3f5dc16331e818b91f5fee5ab5456ea2418d5 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Tue, 17 Dec 2024 17:28:10 -0500 Subject: [PATCH 57/73] save current --- IO_RSproto.cpp | 119 +++++++++++++++++++++++++------------------------ IO_RSproto.h | 6 +-- 2 files changed, 63 insertions(+), 62 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index 39020619..73ae2028 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -41,19 +41,25 @@ taskBuffer::~taskBuffer() // destructor } -void taskBuffer::doCommand(uint8_t *commandBuffer, int commandSize) { +void taskBuffer::doCommand(char *commandBuffer, int commandSize) { for (taskBuffer * t=first;t;t=t->next) t->doCommand2(commandBuffer,commandSize); } -void taskBuffer::doCommand2(uint8_t *commandBuffer, int commandSize) { +void taskBuffer::doCommand2(char *commandBuffer, int commandSize) { // process commands here to be sent //_serial->begin(115200); //ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - if (_txPin != -1) digitalWrite(_txPin,HIGH); - serial->write(commandBuffer, 7); - //serial->flush(); - if (_txPin != -1) digitalWrite(_txPin,LOW); + //int counter = 0; + //RSproto *bus = RSproto::findBus(0); + //RSprotonode *node = bus->findNode(commandSize); + //while (node->resFlag == 0 && counter < 5) { + if (_txPin != -1) digitalWrite(_txPin,HIGH); + serial->print(commandBuffer); + if (_txPin != -1) digitalWrite(_txPin,LOW); + //counter++; + //} + } void taskBuffer::init(HardwareSerial &hwSerial, unsigned long baud, int8_t txPin) { @@ -73,7 +79,7 @@ void taskBuffer::loop2() { while (serial->available()) { char ch = serial->read(); if (!inCommandPayload) { - if (ch == STARTBYTE) { + if (ch == '<') { inCommandPayload = PAYLOAD_NORMAL; bufferLength = 0; buffer[0] = '\0'; @@ -94,7 +100,7 @@ void taskBuffer::loop2() { inCommandPayload++; } if (inCommandPayload == PAYLOAD_NORMAL) { - if (ch == ENDBYTE) { + if (ch == '>') { buffer[bufferLength] = '\0'; parseRx(buffer); inCommandPayload = PAYLOAD_FALSE; @@ -125,17 +131,33 @@ void taskBuffer::parseOne(uint8_t *buf) { while (buf[0] == '<' || buf[0] == ' ') buf++; // strip off any number of < or spaces - uint8_t toNode = buf[0]; + char curBuff[10]; + byte outArray[25]; + int chrCntr = 0; + int byteCntr = 0; + for (int i = 0; i< 200; i++) { + if (buf[i] == '\n') break; + else if (buf[i] == 0x20) { + chrCntr = 0; + sscanf(curBuff, "%d", &outArray[byteCntr]); + byteCntr++; + } else { + curBuff[chrCntr] = buffer[i]; + chrCntr++; + } + } + + uint8_t toNode = outArray[0]; if (toNode != 0) return; // not for master - uint8_t fromNode = buf[1]; + uint8_t fromNode = outArray[1]; if (fromNode == 0) return; // why did out own data come round the ring back to us? - uint8_t opcode = buf[2]; + uint8_t opcode = outArray[2]; RSproto *bus = RSproto::findBus(0); RSprotonode *node = bus->findNode(fromNode); switch (opcode) { case EXIOPINS: - {node->_numDigitalPins = buf[3]; - node->_numAnaloguePins = buf[4]; + {node->_numDigitalPins = outArray[3]; + node->_numAnaloguePins = outArray[4]; // See if we already have suitable buffers assigned if (node->_numDigitalPins>0) { @@ -182,15 +204,15 @@ void taskBuffer::parseOne(uint8_t *buf) { break;} case EXIOINITA: { for (int i = 3; i < node->_numAnaloguePins; i++) { - node->_analoguePinMap[i] = buf[i]; + node->_analoguePinMap[i] = outArray[i]; } node->resFlag = 1; break; } case EXIOVER: { - node->_majorVer = buf[3]; - node->_minorVer = buf[4]; - node->_patchVer = buf[5]; + node->_majorVer = outArray[3]; + node->_minorVer = outArray[4]; + node->_patchVer = outArray[5]; node->resFlag = 1; break; } @@ -204,14 +226,14 @@ void taskBuffer::parseOne(uint8_t *buf) { } case EXIORDD: { for (int i = 3; i < (node->_numDigitalPins+7)/8; i++) { - node->_digitalInputStates[i-3] = buf[i]; + node->_digitalInputStates[i-3] = outArray[i]; } node->resFlag = 1; break; } case EXIORDAN: { for (int i = 3; i < node->_numAnaloguePins*2; i++) { - node->_analogueInputBuffer[i-3] = buf[i]; + node->_analogueInputBuffer[i-3] = outArray[i]; } node->resFlag = 1; break; @@ -299,9 +321,10 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun int pin = vpin - _firstVpin; uint8_t pullup = (uint8_t)params[0]; - uint8_t outBuffer[] = {0xFD, _nodeID, 0, EXIODPUP, (uint8_t)pin, pullup, 0xFE}; + char buff[25]; + sprintf(buff, "<%i %i %i %i %i>", _nodeID, 0, EXIODPUP, pin, pullup); unsigned long startMillis = millis(); - task->doCommand(outBuffer,7); + task->doCommand(buff,_nodeID); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); @@ -314,9 +337,10 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun int RSprotonode::_configureAnalogIn(VPIN vpin) { int pin = vpin - _firstVpin; //RSproto *mainrs = RSproto::findBus(_busNo); - uint8_t commandBuffer[] = {0xFD, _nodeID, 0, EXIOENAN, (uint8_t) pin, 0xFE}; + char buff[25]; + sprintf(buff, "<%i %i %i %i %i %i>", _nodeID, 0, EXIOENAN, pin, _firstVpin); unsigned long startMillis = millis(); - task->doCommand(commandBuffer, 6); + task->doCommand(buff, _nodeID); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); @@ -327,33 +351,26 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun } void RSprotonode::_begin() { - uint8_t commandBuffer[] = {0xFD, _nodeID, 0, EXIOINIT, (uint8_t)_nPins, (uint8_t)(_firstVpin & 0xFF), (uint8_t)(_firstVpin >> 8), 0xFE}; + char buff[25]; + sprintf(buff, "<%i %i %i %i %i %i %i>", _nodeID, 0, EXIOINIT, _nPins, (_firstVpin & 0xFF), (_firstVpin >> 8)); unsigned long startMillis = millis(); - task->doCommand(commandBuffer,8); + task->doCommand(buff,_nodeID); while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOINIT"), _nodeID); } resFlag = 0; - commandBuffer[0] = 0xFD; - commandBuffer[1] = _nodeID; - commandBuffer[2] = 0; - commandBuffer[3] = EXIOINITA; - commandBuffer[4] = 0xFE; + sprintf(buff, "<%i %i %i>", _nodeID, 0, EXIOINITA); startMillis = millis(); - task->doCommand(commandBuffer,5); + task->doCommand(buff,_nodeID); while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOINITA"), _nodeID); } resFlag = 0; - commandBuffer[0] = 0xFD; - commandBuffer[1] = _nodeID; - commandBuffer[2] = 0; - commandBuffer[3] = EXIOVER; - commandBuffer[4] = 0xFE; + sprintf(buff, "<%i %i %i>", _nodeID, 0, EXIOVER); startMillis = millis(); - task->doCommand(commandBuffer,3); + task->doCommand(buff,_nodeID); while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); @@ -376,16 +393,10 @@ int RSprotonode::_read(VPIN vpin) { void RSprotonode::_write(VPIN vpin, int value) { if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; - uint8_t digitalOutBuffer[7]; - digitalOutBuffer[0] = 0xFD; - digitalOutBuffer[1] = _nodeID; - digitalOutBuffer[2] = 0; - digitalOutBuffer[3] = EXIOWRD; - digitalOutBuffer[4] = (uint8_t) pin; - digitalOutBuffer[5] = (uint8_t) value; - digitalOutBuffer[6] = 0xFE; + char buff[25]; + sprintf(buff, "<%i %i %i %i %i>", _nodeID, 0, EXIOWRD, pin, value); unsigned long startMillis = millis(); - task->doCommand(digitalOutBuffer,7); + task->doCommand(buff,_nodeID); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); @@ -407,21 +418,11 @@ void RSprotonode::_write(VPIN vpin, int value) { } void RSprotonode::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) { - uint8_t servoBuffer[11]; int pin = vpin - _firstVpin; - servoBuffer[0] = 0xFD; - servoBuffer[1] = _nodeID; - servoBuffer[2] = 0; - servoBuffer[3] = EXIOWRAN; - servoBuffer[4] = (uint8_t) pin; - servoBuffer[5] = (uint8_t) value & 0xFF; - servoBuffer[6] = (uint8_t) value >> 8; - servoBuffer[7] = (uint8_t) profile; - servoBuffer[8] = (uint8_t) duration & 0xFF; - servoBuffer[9] = (uint8_t) duration >> 8; - servoBuffer[10] = 0xFE; + char buff[25]; + sprintf(buff, "<%i %i %i %i %i %i %i>", _nodeID, 0, EXIOWRAN, pin, value, profile, duration); unsigned long startMillis = millis(); - task->doCommand(servoBuffer,11); + task->doCommand(buff,_nodeID); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); diff --git a/IO_RSproto.h b/IO_RSproto.h index 4c67eddf..bcd68c5f 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -39,7 +39,7 @@ * * firstVPIN = first vpin in block allocated to this device * numVPINs = number of vpins - * nodeID = 1-252 + * nodeID = 1-251 */ #ifndef IO_RS485_H @@ -111,14 +111,14 @@ static taskBuffer *first; STARTBYTE = 0xFD, ENDBYTE = 0xFE, }; - void doCommand2(uint8_t *commandBuffer=NULL, int commandSize=0); + void doCommand2(char *commandBuffer, int commandSize=0); void loop2(); void parseRx(uint8_t *buf); void parseOne(uint8_t *buf); public: taskBuffer(Stream * myserial); ~taskBuffer(); - static void doCommand(uint8_t *commandBuffer=NULL, int commandSize=0); + static void doCommand(char *commandBuffer, int commandSize=0); static void init(HardwareSerial &hwSerial, unsigned long baud, int8_t txPin=-1); static void loop(); }; From aa7cf84e2d501779f32d950c34a05a2ea32bbeaf Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Wed, 18 Dec 2024 11:06:30 -0500 Subject: [PATCH 58/73] save current --- IO_RSproto.cpp | 21 +++++++++++---------- IO_RSproto.h | 6 +++--- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index 73ae2028..9e62d964 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -246,11 +246,12 @@ void taskBuffer::parseOne(uint8_t *buf) { ************************************************************/ // Constructor for RSproto -RSproto::RSproto(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin) { +RSproto::RSproto(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin, int cycleTime) { _serial = &serial; _baud = baud; _txPin = txPin; _busNo = busNo; + _cycleTime = cycleTime; task->init(serial, baud, txPin); if (_waitA < 3) _waitA = 3; // Add device to HAL device chain @@ -275,8 +276,8 @@ void RSproto::_loop(unsigned long currentMicros) { //} - //if (_currentMicros - _cycleStartTime < _cycleTime) return; - //_cycleStartTime = _currentMicros; + if (_currentMicros - _cycleStartTime < _cycleTime) return; + _cycleStartTime = _currentMicros; //if (_currentNode == NULL) return; task->loop(); @@ -322,7 +323,7 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun uint8_t pullup = (uint8_t)params[0]; char buff[25]; - sprintf(buff, "<%i %i %i %i %i>", _nodeID, 0, EXIODPUP, pin, pullup); + sprintf(buff, "<5|%i %i %i %i %i>", _nodeID, 0, EXIODPUP, pin, pullup); unsigned long startMillis = millis(); task->doCommand(buff,_nodeID); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now @@ -338,7 +339,7 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun int pin = vpin - _firstVpin; //RSproto *mainrs = RSproto::findBus(_busNo); char buff[25]; - sprintf(buff, "<%i %i %i %i %i %i>", _nodeID, 0, EXIOENAN, pin, _firstVpin); + sprintf(buff, "<6|%i %i %i %i %i %i>", _nodeID, 0, EXIOENAN, pin, _firstVpin); unsigned long startMillis = millis(); task->doCommand(buff, _nodeID); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now @@ -352,7 +353,7 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun void RSprotonode::_begin() { char buff[25]; - sprintf(buff, "<%i %i %i %i %i %i %i>", _nodeID, 0, EXIOINIT, _nPins, (_firstVpin & 0xFF), (_firstVpin >> 8)); + sprintf(buff, "<6|%i %i %i %i %i %i>", _nodeID, 0, EXIOINIT, _nPins, (_firstVpin & 0xFF), (_firstVpin >> 8)); unsigned long startMillis = millis(); task->doCommand(buff,_nodeID); while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now @@ -360,7 +361,7 @@ void RSprotonode::_begin() { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOINIT"), _nodeID); } resFlag = 0; - sprintf(buff, "<%i %i %i>", _nodeID, 0, EXIOINITA); + sprintf(buff, "<3|%i %i %i>", _nodeID, 0, EXIOINITA); startMillis = millis(); task->doCommand(buff,_nodeID); while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now @@ -368,7 +369,7 @@ void RSprotonode::_begin() { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOINITA"), _nodeID); } resFlag = 0; - sprintf(buff, "<%i %i %i>", _nodeID, 0, EXIOVER); + sprintf(buff, "<3|%i %i %i>", _nodeID, 0, EXIOVER); startMillis = millis(); task->doCommand(buff,_nodeID); while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now @@ -394,7 +395,7 @@ void RSprotonode::_write(VPIN vpin, int value) { if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; char buff[25]; - sprintf(buff, "<%i %i %i %i %i>", _nodeID, 0, EXIOWRD, pin, value); + sprintf(buff, "<5|%i %i %i %i %i>", _nodeID, 0, EXIOWRD, pin, value); unsigned long startMillis = millis(); task->doCommand(buff,_nodeID); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now @@ -420,7 +421,7 @@ void RSprotonode::_write(VPIN vpin, int value) { void RSprotonode::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) { int pin = vpin - _firstVpin; char buff[25]; - sprintf(buff, "<%i %i %i %i %i %i %i>", _nodeID, 0, EXIOWRAN, pin, value, profile, duration); + sprintf(buff, "<7|%i %i %i %i %i %i %i>", _nodeID, 0, EXIOWRAN, pin, value, profile, duration); unsigned long startMillis = millis(); task->doCommand(buff,_nodeID); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now diff --git a/IO_RSproto.h b/IO_RSproto.h index bcd68c5f..e69db0ec 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -282,8 +282,8 @@ class RSproto : public IODevice { } public: - static void create(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin=-1) { - new RSproto(busNo, serial, baud, txPin); + static void create(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin=-1, int cycleTime=500) { + new RSproto(busNo, serial, baud, txPin, cycleTime); } HardwareSerial* _serial; int _CommMode = 0; @@ -356,7 +356,7 @@ class RSproto : public IODevice { } protected: - RSproto(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin); + RSproto(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin, int cycleTime); public: From 8414ff737c1382528e62fbfafed7caa1746ba84d Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Wed, 18 Dec 2024 17:53:02 -0500 Subject: [PATCH 59/73] save current --- IO_RSproto.cpp | 157 ++++++++++++++++++++++++++++++------------------- IO_RSproto.h | 11 ++-- 2 files changed, 102 insertions(+), 66 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index 9e62d964..54833893 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -41,6 +41,65 @@ taskBuffer::~taskBuffer() // destructor } +int taskBuffer::getCharsRightOfPosition(char* str, char position, char* outStr, int size) { + //if (position >= 0 && position < strlen(str)) { + int pos; + char retStr[size]; + for (int i = 0; str[i] != '\0'; i++) { + if (str[i] == position) { + pos = i; + break; // Exit the loop once the character is found + } + } + int i; + for (i = 0; i < size; i++) { + retStr[i] = str[i+pos+1]; + } + memcpy(outStr, retStr, i+pos+1); + return i+pos+2; + //} +} + +void taskBuffer::getCharsLeft(char *str, char position, char *result) { + int pos; + for (int i = 0; str[i] != '\0'; i++) { + if (str[i] == position) { + pos = i; + break; // Exit the loop once the character is found + } + } + if (pos >= 0 && pos < strlen(str)) { + for (int i = 0; i < strlen(str); i++) { + if (i < pos) result[i] = str[i]; + } + } +} + +int taskBuffer::getValues(char *buf, int lenBuf, int fieldCnt, int *outArray) { + char curBuff[400]; + memset(curBuff, '\0', 25); + int byteCntr = 0; + int counter = 0; + for (int i = 0; i< lenBuf; i++) { + + if (buf[i] == ' ' || buf[i] == '\0'){ + //outArray[counter] = 0x00; + outArray[byteCntr] = atoi(curBuff); + //DIAG(F("byte: %i"),outArray[byteCntr]); + byteCntr++; + memset(curBuff, '\0', sizeof(curBuff)); + counter = 0; + } else { + curBuff[counter] = buf[i]; + counter++; + } + if (byteCntr > fieldCnt || buf[i] == '\0') { + break; + } + } + return byteCntr; +} + void taskBuffer::doCommand(char *commandBuffer, int commandSize) { for (taskBuffer * t=first;t;t=t->next) t->doCommand2(commandBuffer,commandSize); } @@ -76,88 +135,62 @@ void taskBuffer::loop() { void taskBuffer::loop2() { // process received commands here + char ch; while (serial->available()) { - char ch = serial->read(); + ch = serial->read(); + //RS485_SERIAL.write(ch,1); // send round the ring in case not ours if (!inCommandPayload) { if (ch == '<') { inCommandPayload = PAYLOAD_NORMAL; bufferLength = 0; - buffer[0] = '\0'; + //tmpBuffer[0] = '\0'; } } else { // if (inCommandPayload) - if (bufferLength < (COMMAND_BUFFER_SIZE-1)) - buffer[bufferLength++] = ch; - if (inCommandPayload > PAYLOAD_NORMAL) { - if (inCommandPayload > 32 + 2) { // String way too long - ch = ENDBYTE; // we end this nonsense - inCommandPayload = PAYLOAD_NORMAL; - DIAG(F("Parse error: Unbalanced string")); - // fall through to ending parsing below - } else if (ch == '"') { // String end - inCommandPayload = PAYLOAD_NORMAL; - continue; // do not fall through - } else - inCommandPayload++; - } if (inCommandPayload == PAYLOAD_NORMAL) { if (ch == '>') { - buffer[bufferLength] = '\0'; - parseRx(buffer); + //tmpBuffer[bufferLength] = '\0'; + + char chrSize[3]; + getCharsLeft(buffer,'|', chrSize); + char chrSend[400]; + int remainder = getCharsRightOfPosition(buffer,'|',chrSend, sizeof(buffer)); + DIAG(F("%s:%i:%i"),chrSend, remainder,atoi(chrSize)); + parseRx(chrSend, remainder, atoi(chrSize)); + memset(buffer, '\0', sizeof(buffer)); + bufferLength = 0; inCommandPayload = PAYLOAD_FALSE; - break; - } else if (ch == '"') { - inCommandPayload = PAYLOAD_STRING; + } } + if (bufferLength < (COMMAND_BUFFER_SIZE-1) && inCommandPayload == PAYLOAD_NORMAL) { + buffer[bufferLength] = ch; + bufferLength++; + } } } } -void taskBuffer::parseRx(uint8_t *buf) { +void taskBuffer::parseRx(char * rxbuffer, int rxBufLen, int fieldCnt) { // pass on what we got - bool found = (buf[0] != STARTBYTE); - for (byte *b=buf; b[0] != '\0'; b++) { - if (found) { - parseOne(b); - found=false; - } - if (b[0] == STARTBYTE) - found = true; - } + int outValues[fieldCnt]; + memset(outValues, 0, sizeof(outValues)); + int realCnt = getValues(rxbuffer, rxBufLen, fieldCnt, outValues); + parseOne(outValues); } -void taskBuffer::parseOne(uint8_t *buf) { +void taskBuffer::parseOne(int *buf) { // finaly, process the darn data - while (buf[0] == '<' || buf[0] == ' ') - buf++; // strip off any number of < or spaces - - char curBuff[10]; - byte outArray[25]; - int chrCntr = 0; - int byteCntr = 0; - for (int i = 0; i< 200; i++) { - if (buf[i] == '\n') break; - else if (buf[i] == 0x20) { - chrCntr = 0; - sscanf(curBuff, "%d", &outArray[byteCntr]); - byteCntr++; - } else { - curBuff[chrCntr] = buffer[i]; - chrCntr++; - } - } - - uint8_t toNode = outArray[0]; + uint8_t toNode = buf[0]; if (toNode != 0) return; // not for master - uint8_t fromNode = outArray[1]; + uint8_t fromNode = buf[1]; if (fromNode == 0) return; // why did out own data come round the ring back to us? - uint8_t opcode = outArray[2]; + uint8_t opcode = buf[2]; RSproto *bus = RSproto::findBus(0); RSprotonode *node = bus->findNode(fromNode); switch (opcode) { case EXIOPINS: - {node->_numDigitalPins = outArray[3]; - node->_numAnaloguePins = outArray[4]; + {node->_numDigitalPins = buf[3]; + node->_numAnaloguePins = buf[4]; // See if we already have suitable buffers assigned if (node->_numDigitalPins>0) { @@ -204,15 +237,15 @@ void taskBuffer::parseOne(uint8_t *buf) { break;} case EXIOINITA: { for (int i = 3; i < node->_numAnaloguePins; i++) { - node->_analoguePinMap[i] = outArray[i]; + node->_analoguePinMap[i] = buf[i]; } node->resFlag = 1; break; } case EXIOVER: { - node->_majorVer = outArray[3]; - node->_minorVer = outArray[4]; - node->_patchVer = outArray[5]; + node->_majorVer = buf[3]; + node->_minorVer = buf[4]; + node->_patchVer = buf[5]; node->resFlag = 1; break; } @@ -226,14 +259,14 @@ void taskBuffer::parseOne(uint8_t *buf) { } case EXIORDD: { for (int i = 3; i < (node->_numDigitalPins+7)/8; i++) { - node->_digitalInputStates[i-3] = outArray[i]; + node->_digitalInputStates[i-3] = buf[i]; } node->resFlag = 1; break; } case EXIORDAN: { for (int i = 3; i < node->_numAnaloguePins*2; i++) { - node->_analogueInputBuffer[i-3] = outArray[i]; + node->_analogueInputBuffer[i-3] = buf[i]; } node->resFlag = 1; break; diff --git a/IO_RSproto.h b/IO_RSproto.h index e69db0ec..52fd39db 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -53,7 +53,7 @@ class RSprotonode; #ifndef COMMAND_BUFFER_SIZE - #define COMMAND_BUFFER_SIZE 100 + #define COMMAND_BUFFER_SIZE 400 #endif /********************************************************************** @@ -86,7 +86,7 @@ static taskBuffer *first; uint8_t _nodeID; uint8_t _commandType; byte bufferLength; - byte buffer[COMMAND_BUFFER_SIZE]; + char buffer[COMMAND_BUFFER_SIZE]; taskBuffer *next; uint8_t startChar[1] = {0xFD}; uint8_t endChar[1] = {0xFE}; @@ -111,10 +111,13 @@ static taskBuffer *first; STARTBYTE = 0xFD, ENDBYTE = 0xFE, }; + int getCharsRightOfPosition(char* str, char position, char* outStr, int size); + void getCharsLeft(char *str, char position, char *result); + int getValues(char *buf, int lenBuf, int fieldCnt, int *outArray); void doCommand2(char *commandBuffer, int commandSize=0); void loop2(); - void parseRx(uint8_t *buf); - void parseOne(uint8_t *buf); + void parseRx(char * rxbuffer, int rxBufLen, int fieldCnt); + void parseOne(int *buf); public: taskBuffer(Stream * myserial); ~taskBuffer(); From 415dc2674ab90fb88cd0da449b51ab2c38e46015 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Thu, 19 Dec 2024 17:28:54 -0500 Subject: [PATCH 60/73] save current --- IO_RSproto.cpp | 432 +++++++++++++++++++++++++++++-------------------- IO_RSproto.h | 113 ++++++++++--- 2 files changed, 342 insertions(+), 203 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index 54833893..2f765590 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -27,13 +27,20 @@ static const byte PAYLOAD_STRING = 2; taskBuffer * taskBuffer::first=NULL; -taskBuffer::taskBuffer(Stream * myserial) +taskBuffer::taskBuffer(unsigned long taskID, int *commandBuffer) { - serial = myserial; + _taskID = taskID; + memset(commandArray, 0, ARRAY_SIZE); + memcpy(commandArray, commandBuffer, ARRAY_SIZE); + next=first; first=this; - bufferLength=0; - inCommandPayload=PAYLOAD_FALSE; + + RSproto *bus = RSproto::findBus(0); + if (bus != NULL) { + bus->addTask(this); + return; + } } taskBuffer::~taskBuffer() @@ -41,27 +48,20 @@ taskBuffer::~taskBuffer() // destructor } -int taskBuffer::getCharsRightOfPosition(char* str, char position, char* outStr, int size) { - //if (position >= 0 && position < strlen(str)) { - int pos; - char retStr[size]; - for (int i = 0; str[i] != '\0'; i++) { - if (str[i] == position) { - pos = i; - break; // Exit the loop once the character is found - } - } - int i; - for (i = 0; i < size; i++) { - retStr[i] = str[i+pos+1]; +void RSproto::remove_nulls(char *str, int len) { + int i, j = 0; + for (i = 0; i fieldCnt || buf[i] == '\0') { - break; - } - } - return byteCntr; -} - -void taskBuffer::doCommand(char *commandBuffer, int commandSize) { - for (taskBuffer * t=first;t;t=t->next) t->doCommand2(commandBuffer,commandSize); -} - -void taskBuffer::doCommand2(char *commandBuffer, int commandSize) { - // process commands here to be sent - - //_serial->begin(115200); - //ArduinoPins::fastWriteDigital(bus->_txPin, HIGH); - //int counter = 0; - //RSproto *bus = RSproto::findBus(0); - //RSprotonode *node = bus->findNode(commandSize); - //while (node->resFlag == 0 && counter < 5) { - if (_txPin != -1) digitalWrite(_txPin,HIGH); - serial->print(commandBuffer); - if (_txPin != -1) digitalWrite(_txPin,LOW); - //counter++; - //} +void taskBuffer::doCommand(unsigned long taskID, int *commandBuffer) { + // add commands here to be sent + new taskBuffer(taskID, commandBuffer); } -void taskBuffer::init(HardwareSerial &hwSerial, unsigned long baud, int8_t txPin) { - hwSerial.begin(baud, SERIAL_8N1); - new taskBuffer(&hwSerial); - for (taskBuffer * t=first;t;t=t->next) t->_txPin = txPin; - if (txPin != -1) pinMode(txPin, OUTPUT); - if (txPin != -1) digitalWrite(txPin, LOW); -} - -void taskBuffer::loop() { - for (taskBuffer * t=first;t;t=t->next) t->loop2(); -} - -void taskBuffer::loop2() { - // process received commands here - char ch; - while (serial->available()) { - ch = serial->read(); - //RS485_SERIAL.write(ch,1); // send round the ring in case not ours - if (!inCommandPayload) { - if (ch == '<') { - inCommandPayload = PAYLOAD_NORMAL; - bufferLength = 0; - //tmpBuffer[0] = '\0'; - } - } else { // if (inCommandPayload) - if (inCommandPayload == PAYLOAD_NORMAL) { - if (ch == '>') { - //tmpBuffer[bufferLength] = '\0'; - - char chrSize[3]; - getCharsLeft(buffer,'|', chrSize); - char chrSend[400]; - int remainder = getCharsRightOfPosition(buffer,'|',chrSend, sizeof(buffer)); - DIAG(F("%s:%i:%i"),chrSend, remainder,atoi(chrSize)); - parseRx(chrSend, remainder, atoi(chrSize)); - memset(buffer, '\0', sizeof(buffer)); - bufferLength = 0; - inCommandPayload = PAYLOAD_FALSE; - - } - } - if (bufferLength < (COMMAND_BUFFER_SIZE-1) && inCommandPayload == PAYLOAD_NORMAL) { - buffer[bufferLength] = ch; - bufferLength++; - } - } - } -} - -void taskBuffer::parseRx(char * rxbuffer, int rxBufLen, int fieldCnt) { - // pass on what we got - int outValues[fieldCnt]; - memset(outValues, 0, sizeof(outValues)); - int realCnt = getValues(rxbuffer, rxBufLen, fieldCnt, outValues); - parseOne(outValues); -} - -void taskBuffer::parseOne(int *buf) { - // finaly, process the darn data - uint8_t toNode = buf[0]; - if (toNode != 0) return; // not for master - uint8_t fromNode = buf[1]; - if (fromNode == 0) return; // why did out own data come round the ring back to us? - uint8_t opcode = buf[2]; - RSproto *bus = RSproto::findBus(0); - RSprotonode *node = bus->findNode(fromNode); - switch (opcode) { +void RSproto::parseRx(int * outArray) { + int nodeFr = (outArray[3] << 8) | outArray[2]; + int AddrCode = (outArray[5] << 8) | outArray[4]; + DIAG(F("From: %i, To: %i"), nodeFr,(outArray[1] << 8) | outArray[0]); + RSprotonode *node = findNode(nodeFr); + switch (AddrCode) { case EXIOPINS: - {node->_numDigitalPins = buf[3]; - node->_numAnaloguePins = buf[4]; + {node->_numDigitalPins = (outArray[7] << 8) | outArray[6]; + node->_numAnaloguePins = (outArray[9] << 8) | outArray[8]; // See if we already have suitable buffers assigned if (node->_numDigitalPins>0) { @@ -201,7 +102,7 @@ void taskBuffer::parseOne(int *buf) { if ((node->_digitalInputStates = (byte*) calloc(digitalBytesNeeded, 1)) != NULL) { node->_digitalPinBytes = digitalBytesNeeded; } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), fromNode, digitalBytesNeeded); + DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), nodeFr, digitalBytesNeeded); //_deviceState = DEVSTATE_FAILED; node->_digitalPinBytes = 0; return; @@ -226,7 +127,7 @@ void taskBuffer::parseOne(int *buf) { node->_analoguePinMap != NULL) { node->_analoguePinBytes = analogueBytesNeeded; } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), fromNode); + DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), nodeFr); //_deviceState = DEVSTATE_FAILED; node->_analoguePinBytes = 0; return; @@ -237,15 +138,15 @@ void taskBuffer::parseOne(int *buf) { break;} case EXIOINITA: { for (int i = 3; i < node->_numAnaloguePins; i++) { - node->_analoguePinMap[i] = buf[i]; + node->_analoguePinMap[i] = (outArray[7] << 8) | outArray[6]; } node->resFlag = 1; break; } case EXIOVER: { - node->_majorVer = buf[3]; - node->_minorVer = buf[4]; - node->_patchVer = buf[5]; + node->_majorVer = (outArray[7] << 8) | outArray[6]; + node->_minorVer = (outArray[9] << 8) | outArray[8]; + node->_patchVer = (outArray[11] << 8) | outArray[10]; node->resFlag = 1; break; } @@ -258,15 +159,15 @@ void taskBuffer::parseOne(int *buf) { break; } case EXIORDD: { - for (int i = 3; i < (node->_numDigitalPins+7)/8; i++) { - node->_digitalInputStates[i-3] = buf[i]; + for (int i = 0; i < (node->_numDigitalPins+7)/8; i=i+2) { + node->_digitalInputStates[i-3] = (outArray[i+1] << 8) | outArray[i]; } node->resFlag = 1; break; } case EXIORDAN: { for (int i = 3; i < node->_numAnaloguePins*2; i++) { - node->_analogueInputBuffer[i-3] = buf[i]; + node->_analogueInputBuffer[i-3] = (outArray[i+1] << 8) | outArray[i]; } node->resFlag = 1; break; @@ -282,11 +183,12 @@ void taskBuffer::parseOne(int *buf) { RSproto::RSproto(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin, int cycleTime) { _serial = &serial; _baud = baud; + _txPin = txPin; _busNo = busNo; _cycleTime = cycleTime; - task->init(serial, baud, txPin); - if (_waitA < 3) _waitA = 3; + bufferLength=0; + inCommandPayload=PAYLOAD_FALSE; // Add device to HAL device chain IODevice::addDevice(this); @@ -301,19 +203,117 @@ RSproto::RSproto(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8 // Work through list of nodes. For each node, in separate loop entries // When the slot time has finished, move on to the next device. */ + +// CRC-16 implementation (replace with your preferred CRC library if needed) +uint16_t RSproto::crc16(uint8_t *data, uint16_t length) { + uint16_t crc = 0xFFFF; + for (uint16_t i = 0; i < length; i++) { + crc ^= data[i]; + for (int j = 0; j < 8; j++) { + bool bit = ((crc & 0x0001) != 0); + crc >>= 1; + if (bit) { + crc ^= 0xA001; + } + } + } + return crc; +} + +void RSproto::sendInstantCommand(int *buf) { + // Calculate CRC for response data + uint16_t response_crc = crc16((uint8_t*)buf, ARRAY_SIZE); + if (_txPin != -1) digitalWrite(_txPin,HIGH); + // Send response data with CRC + for (int i = 0; i < ARRAY_SIZE; i++) { + _serial->write(buf[i]); + + } + _serial->write(response_crc >> 8); + _serial->write(response_crc & 0xFF); + _serial->flush(); + if (_txPin != -1) digitalWrite(_txPin,LOW); + // delete task command after sending, for now + + DIAG(F("SA: %I"),_serial->available()); + if (_serial->available() >= ARRAY_SIZE) { + int received_data[ARRAY_SIZE]; + + // Read data and CRC + for (int i = 0; i < ARRAY_SIZE; i++) { + received_data[i] = _serial->read(); + } + uint16_t received_crc = (_serial->read() << 8) | _serial->read(); + + // Calculate CRC for received data + uint16_t calculated_crc = crc16((uint8_t*)received_data, ARRAY_SIZE); + + // Check CRC validity + if (calculated_crc == received_crc) { + // Data received successfully, process it (e.g., print) + int nodeTo = (received_data[1] << 8) | received_data[0]; + if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. + parseRx(received_data); + } + } else { + DIAG(F("IO_RSproto: CRC Error!")); + } + } +} + void RSproto::_loop(unsigned long currentMicros) { _currentMicros = currentMicros; - - //if (_currentNode == NULL) { - // _currentNode = _nodeListStart; + if (_busy == true) return; + if (_currentTask == NULL) { + _currentTask = _taskListStart; - //} + } if (_currentMicros - _cycleStartTime < _cycleTime) return; _cycleStartTime = _currentMicros; - //if (_currentNode == NULL) return; - task->loop(); - + if (_currentTask != NULL && _currentTask->commandArray[0] != 0) { + + // Calculate CRC for response data + uint16_t response_crc = crc16((uint8_t*)_currentTask->commandArray, ARRAY_SIZE); + if (_txPin != -1) digitalWrite(_txPin,HIGH); + // Send response data with CRC + for (int i = 0; i < ARRAY_SIZE; i++) { + _serial->write(_currentTask->commandArray[i]); + } + _serial->write(response_crc >> 8); + _serial->write(response_crc & 0xFF); + _serial->flush(); + if (_txPin != -1) digitalWrite(_txPin,LOW); + // delete task command after sending, for now + memset(_currentTask->commandArray, 0, ARRAY_SIZE); + _currentTask->_commandSize = 0; + } + + if (_serial->available() >= ARRAY_SIZE) { + int received_data[ARRAY_SIZE]; + + // Read data and CRC + for (int i = 0; i < ARRAY_SIZE; i++) { + received_data[i / sizeof(int)] = _serial->read(); + } + uint16_t received_crc = (_serial->read() << 8) | _serial->read(); + + // Calculate CRC for received data + uint16_t calculated_crc = crc16((uint8_t*)received_data, ARRAY_SIZE); + + // Check CRC validity + if (calculated_crc == received_crc) { + // Data received successfully, process it (e.g., print) + int nodeTo = (received_data[1] << 8) | received_data[0]; + if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. + parseRx(received_data); + } + } else { + DIAG(F("IO_RSproto: CRC Error!")); + } + } + + task->getNext(); } // Link to chain of RSproto instances, left over from RSproto template. @@ -355,26 +355,50 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun int pin = vpin - _firstVpin; uint8_t pullup = (uint8_t)params[0]; - char buff[25]; - sprintf(buff, "<5|%i %i %i %i %i>", _nodeID, 0, EXIODPUP, pin, pullup); - unsigned long startMillis = millis(); - task->doCommand(buff,_nodeID); - while (resFlag == 0 && millis() - startMillis < 500); // blocking for now - if (resFlag != 1) { - DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); - return false; - } - resFlag = 0; - return true; + int buff[ARRAY_SIZE]; + buff[0] = highByte(_nodeID); + buff[1] = lowByte(_nodeID); + buff[2] = highByte(0); + buff[3] = lowByte(0); + buff[4] = highByte(EXIODPUP); + buff[5] = lowByte(EXIODPUP); + buff[6] = highByte(pin); + buff[7] = lowByte(pin); + buff[8] = highByte(pullup); + buff[9] = lowByte(pullup); + unsigned long startMillis = millis(); + RSproto *bus = RSproto::findBus(0); + bus->_busy = true; + bus->sendInstantCommand(buff); + bus->_busy = false; + while (resFlag == 0 && millis() - startMillis < 500); // blocking for now + if (resFlag != 1) { + DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); + return false; + } + resFlag = 0; + return true; } int RSprotonode::_configureAnalogIn(VPIN vpin) { int pin = vpin - _firstVpin; //RSproto *mainrs = RSproto::findBus(_busNo); - char buff[25]; - sprintf(buff, "<6|%i %i %i %i %i %i>", _nodeID, 0, EXIOENAN, pin, _firstVpin); + int buff[ARRAY_SIZE]; + buff[0] = highByte(_nodeID); + buff[1] = lowByte(_nodeID); + buff[2] = highByte(0); + buff[3] = lowByte(0); + buff[4] = highByte(EXIOENAN); + buff[5] = lowByte(EXIOENAN); + buff[6] = highByte(pin); + buff[7] = lowByte(pin); + buff[8] = highByte(_firstVpin); + buff[9] = lowByte(_firstVpin); unsigned long startMillis = millis(); - task->doCommand(buff, _nodeID); + RSproto *bus = RSproto::findBus(0); + bus->_busy = true; + bus->sendInstantCommand(buff); + bus->_busy = false; while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); @@ -385,26 +409,52 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun } void RSprotonode::_begin() { - char buff[25]; - sprintf(buff, "<6|%i %i %i %i %i %i>", _nodeID, 0, EXIOINIT, _nPins, (_firstVpin & 0xFF), (_firstVpin >> 8)); - unsigned long startMillis = millis(); - task->doCommand(buff,_nodeID); + int buff[ARRAY_SIZE]; + buff[0] = highByte(_nodeID); + buff[1] = lowByte(_nodeID); + buff[2] = highByte(0); + buff[3] = lowByte(0); + buff[4] = highByte(EXIOINIT); + buff[5] = lowByte(EXIOINIT); + buff[6] = highByte(_nPins); + buff[7] = lowByte(_nPins); + buff[8] = highByte((_firstVpin & 0xFF)); + buff[9] = lowByte((_firstVpin >> 8)); + unsigned long startMillis = millis(); + RSproto *bus = RSproto::findBus(0); + bus->_busy = true; + bus->sendInstantCommand(buff); + bus->_busy = false; while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOINIT"), _nodeID); } resFlag = 0; - sprintf(buff, "<3|%i %i %i>", _nodeID, 0, EXIOINITA); + buff[0] = highByte(_nodeID); + buff[1] = lowByte(_nodeID); + buff[2] = highByte(0); + buff[3] = lowByte(0); + buff[4] = highByte(EXIOINITA); + buff[5] = lowByte(EXIOINITA); startMillis = millis(); - task->doCommand(buff,_nodeID); + bus->_busy = true; + bus->sendInstantCommand(buff); + bus->_busy = false; while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOINITA"), _nodeID); } resFlag = 0; - sprintf(buff, "<3|%i %i %i>", _nodeID, 0, EXIOVER); + buff[0] = highByte(_nodeID); + buff[1] = lowByte(_nodeID); + buff[2] = highByte(0); + buff[3] = lowByte(0); + buff[4] = highByte(EXIOVER); + buff[5] = lowByte(EXIOVER); startMillis = millis(); - task->doCommand(buff,_nodeID); + bus->_busy = true; + bus->sendInstantCommand(buff); + bus->_busy = false; task->doCommand(bus->taskCounter++, buff); while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); @@ -427,10 +477,20 @@ int RSprotonode::_read(VPIN vpin) { void RSprotonode::_write(VPIN vpin, int value) { if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; - char buff[25]; - sprintf(buff, "<5|%i %i %i %i %i>", _nodeID, 0, EXIOWRD, pin, value); + int buff[ARRAY_SIZE]; + buff[0] = highByte(_nodeID); + buff[1] = lowByte(_nodeID); + buff[2] = highByte(0); + buff[3] = lowByte(0); + buff[4] = highByte(EXIOWRD); + buff[5] = lowByte(EXIOWRD); + buff[6] = highByte(pin); + buff[7] = lowByte(pin); + buff[8] = highByte(value); + buff[9] = lowByte(value); unsigned long startMillis = millis(); - task->doCommand(buff,_nodeID); + RSproto *bus = RSproto::findBus(0); + task->doCommand(bus->taskCounter++, buff); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); @@ -453,10 +513,24 @@ void RSprotonode::_write(VPIN vpin, int value) { void RSprotonode::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) { int pin = vpin - _firstVpin; - char buff[25]; - sprintf(buff, "<7|%i %i %i %i %i %i %i>", _nodeID, 0, EXIOWRAN, pin, value, profile, duration); + int buff[ARRAY_SIZE]; + buff[0] = highByte(_nodeID); + buff[1] = lowByte(_nodeID); + buff[2] = highByte(0); + buff[3] = lowByte(0); + buff[4] = highByte(EXIOWRAN); + buff[5] = lowByte(EXIOWRAN); + buff[6] = highByte(pin); + buff[7] = lowByte(pin); + buff[8] = highByte(value); + buff[9] = lowByte(value); + buff[8] = highByte(profile); + buff[9] = lowByte(profile); + buff[8] = highByte(duration); + buff[9] = lowByte(duration); unsigned long startMillis = millis(); - task->doCommand(buff,_nodeID); + RSproto *bus = RSproto::findBus(0); + task->doCommand(bus->taskCounter++, buff); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); diff --git a/IO_RSproto.h b/IO_RSproto.h index 52fd39db..de7d020f 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -53,7 +53,7 @@ class RSprotonode; #ifndef COMMAND_BUFFER_SIZE - #define COMMAND_BUFFER_SIZE 400 + #define COMMAND_BUFFER_SIZE 900 #endif /********************************************************************** @@ -79,18 +79,26 @@ class RSprotonode; class taskBuffer { private: -static taskBuffer *first; + HardwareSerial * hwSerial; + unsigned long _baud; + static const int ARRAY_SIZE = 254; +public: + static taskBuffer *first; + static taskBuffer *end; RSprotonode *node; + unsigned long _taskID; Stream * serial; uint8_t _txPin; uint8_t _nodeID; uint8_t _commandType; - byte bufferLength; - char buffer[COMMAND_BUFFER_SIZE]; + unsigned long _cycleTimer = 0ul; + taskBuffer *next; uint8_t startChar[1] = {0xFD}; uint8_t endChar[1] = {0xFE}; - byte inCommandPayload; + + int commandArray[ARRAY_SIZE]; + int _commandSize = 0; // EX-IOExpander protocol flags enum { EXIOINIT = 0xE0, // Flag to initialise setup procedure @@ -111,19 +119,34 @@ static taskBuffer *first; STARTBYTE = 0xFD, ENDBYTE = 0xFE, }; - int getCharsRightOfPosition(char* str, char position, char* outStr, int size); - void getCharsLeft(char *str, char position, char *result); - int getValues(char *buf, int lenBuf, int fieldCnt, int *outArray); - void doCommand2(char *commandBuffer, int commandSize=0); - void loop2(); - void parseRx(char * rxbuffer, int rxBufLen, int fieldCnt); - void parseOne(int *buf); -public: - taskBuffer(Stream * myserial); + + + + unsigned long getTaskID() { + return _taskID; + } + void setTaskID(unsigned long taskID) { + _taskID = taskID; + } + taskBuffer *getNext() { + return next; + } + + void setNext(taskBuffer *task) { + next = task; + } + void addTask(taskBuffer *newTask) { + if (!first) + first = newTask; + if (!end) + end = newTask; + else + end->setNext(newTask); + //DIAG(F("RSproto: 260h nodeID:%d _nodeListStart:%d _nodeListEnd:%d"), newNode, _nodeListStart, _nodeListEnd); + } + taskBuffer(unsigned long taskID, int *commandBuffer); ~taskBuffer(); - static void doCommand(char *commandBuffer, int commandSize=0); - static void init(HardwareSerial &hwSerial, unsigned long baud, int8_t txPin=-1); - static void loop(); + void doCommand(unsigned long taskID, int *commandBuffer); }; @@ -148,7 +171,6 @@ class RSprotonode : public IODevice { RSproto *bus; taskBuffer *task; HardwareSerial* _serial; - // EX-IOExpander protocol flags enum { EXIOINIT = 0xE0, // Flag to initialise setup procedure EXIORDY = 0xE1, // Flag we have completed setup procedure, also for EX-IO to ACK setup @@ -163,7 +185,7 @@ class RSprotonode : public IODevice { EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM) EXIOERR = 0xEF, // Flag we've received an error }; - + static const int ARRAY_SIZE = 254; public: static RSprotonode *_nodeList; enum ProfileType : int { @@ -253,7 +275,10 @@ class RSproto : public IODevice { unsigned long _byteTransmitTime; // time in us for transmission of one byte int _operationCount = 0; int _refreshOperation = 0; - + byte bufferLength; + static const int ARRAY_SIZE = 254; + int buffer[ARRAY_SIZE]; + byte inCommandPayload; static RSproto *_busList; // linked list of defined bus instances bool waitReceive = false; int _waitCounter = 0; @@ -274,6 +299,7 @@ class RSproto : public IODevice { RSprotonode *_nodeListStart = NULL, *_nodeListEnd = NULL; RSprotonode *_currentNode = NULL; + taskBuffer *_taskListStart = NULL, *_taskListEnd = NULL, *_currentTask=NULL; uint16_t _receiveDataIndex = 0; // Index of next data byte to be received. RSproto *_nextBus = NULL; // Pointer to next bus instance in list. @@ -283,8 +309,28 @@ class RSproto : public IODevice { if (fail) _deviceState = DEVSTATE_FAILED; } - + public: +uint16_t crc16(uint8_t *data, uint16_t length); + void remove_nulls(char *str, int len); + int getCharsLeft(char *str, char position); + void parseRx(int * outArray); + void sendInstantCommand(int *buf); + // EX-IOExpander protocol flags + enum { + EXIOINIT = 0xE0, // Flag to initialise setup procedure + EXIORDY = 0xE1, // Flag we have completed setup procedure, also for EX-IO to ACK setup + EXIODPUP = 0xE2, // Flag we're sending digital pin pullup configuration + EXIOVER = 0xE3, // Flag to get version + EXIORDAN = 0xE4, // Flag to read an analogue input + EXIOWRD = 0xE5, // Flag for digital write + EXIORDD = 0xE6, // Flag to read digital input + EXIOENAN = 0xE7, // Flag to enable an analogue pin + EXIOINITA = 0xE8, // Flag we're receiving analogue pin mappings + EXIOPINS = 0xE9, // Flag we're receiving pin counts for buffers + EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM) + EXIOERR = 0xEF, // Flag we've received an error + }; static void create(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin=-1, int cycleTime=500) { new RSproto(busNo, serial, baud, txPin, cycleTime); } @@ -298,10 +344,14 @@ class RSproto : public IODevice { unsigned long _baud; int taskCnt = 0; uint8_t initBuffer[1] = {0xFE}; - + unsigned long taskCounter=0; // Device-specific initialisation void _begin() override { - + _serial->begin(_baud, SERIAL_8N1); + if (_txPin >0) { + pinMode(_txPin, OUTPUT); + digitalWrite(_txPin, LOW); + } #if defined(RSproto_STM_OK) pinMode(RSproto_STM_OK, OUTPUT); ArduinoPins::fastWriteDigital(RSproto_STM_OK,LOW); @@ -338,6 +388,13 @@ class RSproto : public IODevice { } return NULL; } + taskBuffer *findTask(uint8_t taskID) { + for (taskBuffer *task = _taskListStart; task != NULL; task = task->getNext()) { + if (task->getTaskID() == taskID) + return task; + } + return NULL; + } bool nodesInitialized() { bool retval = true; @@ -357,7 +414,15 @@ class RSproto : public IODevice { _nodeListEnd->setNext(newNode); //DIAG(F("RSproto: 260h nodeID:%d _nodeListStart:%d _nodeListEnd:%d"), newNode, _nodeListStart, _nodeListEnd); } - +void addTask(taskBuffer *newTask) { + if (!_taskListStart) + _taskListStart = newTask; + if (!_nodeListEnd) + _taskListEnd = newTask; + else + _taskListEnd->setNext(newTask); + //DIAG(F("RSproto: 260h nodeID:%d _nodeListStart:%d _nodeListEnd:%d"), newNode, _nodeListStart, _nodeListEnd); + } protected: RSproto(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin, int cycleTime); From da221eb9e72eb0793c0e10fb61339c024231c11a Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Fri, 20 Dec 2024 17:06:56 -0500 Subject: [PATCH 61/73] passes CRC --- IO_RSproto.cpp | 351 +++++++++++++++++++++++++++++-------------------- IO_RSproto.h | 21 ++- 2 files changed, 220 insertions(+), 152 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index 2f765590..a40a4215 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -27,11 +27,12 @@ static const byte PAYLOAD_STRING = 2; taskBuffer * taskBuffer::first=NULL; -taskBuffer::taskBuffer(unsigned long taskID, int *commandBuffer) +taskBuffer::taskBuffer(unsigned long taskID, uint8_t *commandBuffer, int byteCount) { _taskID = taskID; - memset(commandArray, 0, ARRAY_SIZE); - memcpy(commandArray, commandBuffer, ARRAY_SIZE); + _byteCount = byteCount; + memset(commandArray, 0, byteCount); + memcpy(commandArray, commandBuffer, byteCount); next=first; first=this; @@ -77,21 +78,21 @@ int RSproto::getCharsLeft(char *str, char position) { else return 0; } -void taskBuffer::doCommand(unsigned long taskID, int *commandBuffer) { +void taskBuffer::doCommand(unsigned long taskID, uint8_t *commandBuffer, int byteCount) { // add commands here to be sent - new taskBuffer(taskID, commandBuffer); + new taskBuffer(taskID, commandBuffer, byteCount); } -void RSproto::parseRx(int * outArray) { - int nodeFr = (outArray[3] << 8) | outArray[2]; - int AddrCode = (outArray[5] << 8) | outArray[4]; - DIAG(F("From: %i, To: %i"), nodeFr,(outArray[1] << 8) | outArray[0]); +void RSproto::parseRx(uint8_t * outArray) { + int nodeFr = outArray[1]; + int AddrCode = outArray[2]; + DIAG(F("From: %i, To: %i"), nodeFr,outArray[0]); RSprotonode *node = findNode(nodeFr); switch (AddrCode) { case EXIOPINS: - {node->_numDigitalPins = (outArray[7] << 8) | outArray[6]; - node->_numAnaloguePins = (outArray[9] << 8) | outArray[8]; + {node->_numDigitalPins = outArray[3]; + node->_numAnaloguePins = outArray[4]; // See if we already have suitable buffers assigned if (node->_numDigitalPins>0) { @@ -137,16 +138,16 @@ void RSproto::parseRx(int * outArray) { node->resFlag = 1; break;} case EXIOINITA: { - for (int i = 3; i < node->_numAnaloguePins; i++) { - node->_analoguePinMap[i] = (outArray[7] << 8) | outArray[6]; + for (int i = 0; i < node->_numAnaloguePins; i++) { + node->_analoguePinMap[i] = outArray[i+3]; } node->resFlag = 1; break; } case EXIOVER: { - node->_majorVer = (outArray[7] << 8) | outArray[6]; - node->_minorVer = (outArray[9] << 8) | outArray[8]; - node->_patchVer = (outArray[11] << 8) | outArray[10]; + node->_majorVer = outArray[3]; + node->_minorVer = outArray[4]; + node->_patchVer = outArray[5]; node->resFlag = 1; break; } @@ -159,15 +160,15 @@ void RSproto::parseRx(int * outArray) { break; } case EXIORDD: { - for (int i = 0; i < (node->_numDigitalPins+7)/8; i=i+2) { - node->_digitalInputStates[i-3] = (outArray[i+1] << 8) | outArray[i]; + for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { + node->_digitalInputStates[i] = outArray[i+3]; } node->resFlag = 1; break; } case EXIORDAN: { - for (int i = 3; i < node->_numAnaloguePins*2; i++) { - node->_analogueInputBuffer[i-3] = (outArray[i+1] << 8) | outArray[i]; + for (int i = 0; i < node->_numAnaloguePins; i++) { + node->_analogueInputBuffer[i] = outArray[i+3]; } node->resFlag = 1; break; @@ -220,43 +221,87 @@ uint16_t RSproto::crc16(uint8_t *data, uint16_t length) { return crc; } -void RSproto::sendInstantCommand(int *buf) { +void RSproto::sendInstantCommand(uint8_t *buf, int byteCount) { // Calculate CRC for response data - uint16_t response_crc = crc16((uint8_t*)buf, ARRAY_SIZE); + uint16_t response_crc = crc16((uint8_t*)buf, byteCount-1); if (_txPin != -1) digitalWrite(_txPin,HIGH); // Send response data with CRC - for (int i = 0; i < ARRAY_SIZE; i++) { + _serial->write(0xFE); + _serial->write(0xFE); + _serial->write(response_crc >> 8); + _serial->write(response_crc & 0xFF); + _serial->write(byteCount); + for (int i = 0; i < byteCount; i++) { _serial->write(buf[i]); } - _serial->write(response_crc >> 8); - _serial->write(response_crc & 0xFF); + _serial->write(0xFD); + _serial->write(0xFD); _serial->flush(); if (_txPin != -1) digitalWrite(_txPin,LOW); // delete task command after sending, for now - DIAG(F("SA: %I"),_serial->available()); - if (_serial->available() >= ARRAY_SIZE) { - int received_data[ARRAY_SIZE]; - - // Read data and CRC - for (int i = 0; i < ARRAY_SIZE; i++) { - received_data[i] = _serial->read(); - } - uint16_t received_crc = (_serial->read() << 8) | _serial->read(); - - // Calculate CRC for received data - uint16_t calculated_crc = crc16((uint8_t*)received_data, ARRAY_SIZE); - - // Check CRC validity - if (calculated_crc == received_crc) { - // Data received successfully, process it (e.g., print) - int nodeTo = (received_data[1] << 8) | received_data[0]; - if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. - parseRx(received_data); + //int received_data[ARRAY_SIZE]; + uint16_t received_crc; + while(_serial->available()) { + if (_serial->available()) { + uint8_t received_data[ARRAY_SIZE]; + + uint16_t calculated_crc; + int byteCount = 100; + + int curByte = _serial->read(); + + if (curByte == 0xFE && flagStart == false) flagStart = true; + else if ( curByte == 0xFE && flagStart == true) { + byteCounter = 0; + flagStarted = true; + flagStart = false; + flagEnded = false; + rxStart = true; + rxEnd = false; + crcPass = false; + }else if (flagStarted) { + crc[0] = curByte; + byteCounter++; + flagStarted = false; + } else if (byteCounter == 1) { + crc[1] = curByte; + received_crc = (crc[0] << 8) | crc[1]; + byteCounter++; + } else if (byteCounter == 2) { + byteCount = curByte; + byteCounter++; + } else if (flagEnded == false && byteCounter >= 3) { + received_data[byteCounter-3] = curByte; + byteCounter++; + } + if (curByte == 0xFD && flagEnd == false) flagEnd = true; + else if ( curByte == 0xFD && flagEnd == true) { + flagEnded = true; + flagEnd = false; + rxEnd = true; + byteCount = byteCounter; + byteCounter = 0; + } + if (flagEnded) { + calculated_crc = crc16((uint8_t*)received_data, byteCount-6); + if (received_crc == calculated_crc) { + crcPass = true; + DIAG(F("CRC PASS")); + } + flagEnded = false; + } + // Check CRC validity + if (crcPass) { + // Data received successfully, process it (e.g., print) + int nodeTo = (received_data[1] << 8) | received_data[0]; + if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. + parseRx(received_data); + } + } else { + //DIAG(F("IO_RSproto: CRC Error!")); } - } else { - DIAG(F("IO_RSproto: CRC Error!")); } } } @@ -271,45 +316,88 @@ void RSproto::_loop(unsigned long currentMicros) { if (_currentMicros - _cycleStartTime < _cycleTime) return; _cycleStartTime = _currentMicros; - if (_currentTask != NULL && _currentTask->commandArray[0] != 0) { + if (_currentTask != NULL && _currentTask->_byteCount > 0) { // Calculate CRC for response data - uint16_t response_crc = crc16((uint8_t*)_currentTask->commandArray, ARRAY_SIZE); + + uint16_t response_crc = crc16((uint8_t*)_currentTask->commandArray, _currentTask->_byteCount-6); if (_txPin != -1) digitalWrite(_txPin,HIGH); // Send response data with CRC - for (int i = 0; i < ARRAY_SIZE; i++) { - _serial->write(_currentTask->commandArray[i]); - } + _serial->write(0xFE); + _serial->write(0xFE); _serial->write(response_crc >> 8); _serial->write(response_crc & 0xFF); + _serial->write(_currentTask->_byteCount); + for (int i = 0; i < _currentTask->_byteCount; i++) { + _serial->write(_currentTask->commandArray[i]); + } + _serial->write(0xFD); + _serial->write(0xFD); _serial->flush(); if (_txPin != -1) digitalWrite(_txPin,LOW); // delete task command after sending, for now - memset(_currentTask->commandArray, 0, ARRAY_SIZE); - _currentTask->_commandSize = 0; + memset(_currentTask->commandArray, 0, _currentTask->_byteCount); + _currentTask->_byteCount = 0; } - if (_serial->available() >= ARRAY_SIZE) { - int received_data[ARRAY_SIZE]; - - // Read data and CRC - for (int i = 0; i < ARRAY_SIZE; i++) { - received_data[i / sizeof(int)] = _serial->read(); + if (_serial->available()) { + uint8_t received_data[ARRAY_SIZE]; + + uint16_t calculated_crc; + int byteCount = 100; + + uint8_t byte_array[byteCount]; + int curByte = _serial->read(); + + if (curByte == 0xFE && flagStart == false) flagStart = true; + else if ( curByte == 0xFE && flagStart == true) { + byteCounter = 0; + flagStarted = true; + flagStart = false; + flagEnded = false; + rxStart = true; + rxEnd = false; + crcPass = false; + }else if (flagStarted) { + crc[0] = curByte; + byteCounter++; + flagStarted = false; + } else if (byteCounter == 1) { + crc[1] = curByte; + received_crc = (crc[0] << 8) | crc[1]; + byteCounter++; + } else if (byteCounter == 2) { + byteCount = curByte; + byteCounter++; + } else if (flagEnded == false && byteCounter >= 3) { + received_data[byteCounter-3] = curByte; + byteCounter++; + } + if (curByte == 0xFD && flagEnd == false) flagEnd = true; + else if ( curByte == 0xFD && flagEnd == true) { + flagEnded = true; + flagEnd = false; + rxEnd = true; + byteCount = byteCounter; + byteCounter = 0; + } + if (flagEnded) { + calculated_crc = crc16((uint8_t*)received_data, byteCount-6); + if (received_crc == calculated_crc) { + DIAG(F("CRC PASS")); + crcPass = true; + }else DIAG(F("CRC Fail %x %x"),received_crc,calculated_crc); + flagEnded = false; } - uint16_t received_crc = (_serial->read() << 8) | _serial->read(); - - // Calculate CRC for received data - uint16_t calculated_crc = crc16((uint8_t*)received_data, ARRAY_SIZE); - // Check CRC validity - if (calculated_crc == received_crc) { + if (crcPass) { // Data received successfully, process it (e.g., print) - int nodeTo = (received_data[1] << 8) | received_data[0]; + int nodeTo = received_data[0]; if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. parseRx(received_data); } } else { - DIAG(F("IO_RSproto: CRC Error!")); + //DIAG(F("IO_RSproto: CRC Error!")); } } @@ -355,21 +443,16 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun int pin = vpin - _firstVpin; uint8_t pullup = (uint8_t)params[0]; - int buff[ARRAY_SIZE]; - buff[0] = highByte(_nodeID); - buff[1] = lowByte(_nodeID); - buff[2] = highByte(0); - buff[3] = lowByte(0); - buff[4] = highByte(EXIODPUP); - buff[5] = lowByte(EXIODPUP); - buff[6] = highByte(pin); - buff[7] = lowByte(pin); - buff[8] = highByte(pullup); - buff[9] = lowByte(pullup); + uint8_t buff[ARRAY_SIZE]; + buff[0] = (_nodeID); + buff[1] = (0); + buff[2] = (EXIODPUP); + buff[3] = (pin); + buff[4] = (pullup); unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); bus->_busy = true; - bus->sendInstantCommand(buff); + bus->sendInstantCommand(buff, 5); bus->_busy = false; while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { @@ -383,21 +466,17 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun int RSprotonode::_configureAnalogIn(VPIN vpin) { int pin = vpin - _firstVpin; //RSproto *mainrs = RSproto::findBus(_busNo); - int buff[ARRAY_SIZE]; - buff[0] = highByte(_nodeID); - buff[1] = lowByte(_nodeID); - buff[2] = highByte(0); - buff[3] = lowByte(0); - buff[4] = highByte(EXIOENAN); - buff[5] = lowByte(EXIOENAN); - buff[6] = highByte(pin); - buff[7] = lowByte(pin); - buff[8] = highByte(_firstVpin); - buff[9] = lowByte(_firstVpin); + uint8_t buff[ARRAY_SIZE]; + buff[0] = (_nodeID); + buff[1] = (0); + buff[2] = (EXIOENAN); + buff[3] = (pin); + buff[4] = highByte(_firstVpin); + buff[5] = lowByte(_firstVpin); unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); bus->_busy = true; - bus->sendInstantCommand(buff); + bus->sendInstantCommand(buff, 6); bus->_busy = false; while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { @@ -409,52 +488,42 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun } void RSprotonode::_begin() { - int buff[ARRAY_SIZE]; - buff[0] = highByte(_nodeID); - buff[1] = lowByte(_nodeID); - buff[2] = highByte(0); - buff[3] = lowByte(0); - buff[4] = highByte(EXIOINIT); - buff[5] = lowByte(EXIOINIT); - buff[6] = highByte(_nPins); - buff[7] = lowByte(_nPins); - buff[8] = highByte((_firstVpin & 0xFF)); - buff[9] = lowByte((_firstVpin >> 8)); + uint8_t buff[ARRAY_SIZE]; + buff[0] = (_nodeID); + buff[1] = (0); + buff[2] = (EXIOINIT); + buff[3] = (_nPins); + buff[4] = ((_firstVpin & 0xFF)); + buff[5] = ((_firstVpin >> 8)); unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); bus->_busy = true; - bus->sendInstantCommand(buff); + bus->sendInstantCommand(buff, 6); bus->_busy = false; while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOINIT"), _nodeID); } resFlag = 0; - buff[0] = highByte(_nodeID); - buff[1] = lowByte(_nodeID); - buff[2] = highByte(0); - buff[3] = lowByte(0); - buff[4] = highByte(EXIOINITA); - buff[5] = lowByte(EXIOINITA); + buff[0] = (_nodeID); + buff[1] = (0); + buff[2] = (EXIOINITA); startMillis = millis(); bus->_busy = true; - bus->sendInstantCommand(buff); + bus->sendInstantCommand(buff,3); bus->_busy = false; while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOINITA"), _nodeID); } resFlag = 0; - buff[0] = highByte(_nodeID); - buff[1] = lowByte(_nodeID); - buff[2] = highByte(0); - buff[3] = lowByte(0); - buff[4] = highByte(EXIOVER); - buff[5] = lowByte(EXIOVER); + buff[0] = (_nodeID); + buff[1] = (0); + buff[2] = (EXIOVER); startMillis = millis(); bus->_busy = true; - bus->sendInstantCommand(buff); - bus->_busy = false; task->doCommand(bus->taskCounter++, buff); + bus->sendInstantCommand(buff,3); + bus->_busy = false; while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); @@ -477,20 +546,15 @@ int RSprotonode::_read(VPIN vpin) { void RSprotonode::_write(VPIN vpin, int value) { if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; - int buff[ARRAY_SIZE]; - buff[0] = highByte(_nodeID); - buff[1] = lowByte(_nodeID); - buff[2] = highByte(0); - buff[3] = lowByte(0); - buff[4] = highByte(EXIOWRD); - buff[5] = lowByte(EXIOWRD); - buff[6] = highByte(pin); - buff[7] = lowByte(pin); - buff[8] = highByte(value); - buff[9] = lowByte(value); + uint8_t buff[ARRAY_SIZE]; + buff[0] = (_nodeID); + buff[1] = (0); + buff[2] = (EXIOWRD); + buff[3] = (pin); + buff[4] = (value); unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); - task->doCommand(bus->taskCounter++, buff); + task->doCommand(bus->taskCounter++, buff,5); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); @@ -513,24 +577,19 @@ void RSprotonode::_write(VPIN vpin, int value) { void RSprotonode::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) { int pin = vpin - _firstVpin; - int buff[ARRAY_SIZE]; - buff[0] = highByte(_nodeID); - buff[1] = lowByte(_nodeID); - buff[2] = highByte(0); - buff[3] = lowByte(0); - buff[4] = highByte(EXIOWRAN); - buff[5] = lowByte(EXIOWRAN); - buff[6] = highByte(pin); - buff[7] = lowByte(pin); - buff[8] = highByte(value); - buff[9] = lowByte(value); - buff[8] = highByte(profile); - buff[9] = lowByte(profile); - buff[8] = highByte(duration); - buff[9] = lowByte(duration); + uint8_t buff[ARRAY_SIZE]; + buff[0] = (_nodeID); + buff[1] = (0); + buff[2] = (EXIOWRAN); + buff[3] = (pin); + buff[4] = highByte(value); + buff[5] = lowByte(value); + buff[6] = (profile); + buff[7] = highByte(duration); + buff[8] = lowByte(duration); unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); - task->doCommand(bus->taskCounter++, buff); + task->doCommand(bus->taskCounter++, buff,9); while (resFlag == 0 && millis() - startMillis < 500); // blocking for now if (resFlag != 1) { DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); diff --git a/IO_RSproto.h b/IO_RSproto.h index de7d020f..5c516e19 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -98,7 +98,7 @@ class taskBuffer uint8_t endChar[1] = {0xFE}; int commandArray[ARRAY_SIZE]; - int _commandSize = 0; + int _byteCount = 0; // EX-IOExpander protocol flags enum { EXIOINIT = 0xE0, // Flag to initialise setup procedure @@ -144,9 +144,9 @@ class taskBuffer end->setNext(newTask); //DIAG(F("RSproto: 260h nodeID:%d _nodeListStart:%d _nodeListEnd:%d"), newNode, _nodeListStart, _nodeListEnd); } - taskBuffer(unsigned long taskID, int *commandBuffer); + taskBuffer(unsigned long taskID, uint8_t *commandBuffer, int byteCount); ~taskBuffer(); - void doCommand(unsigned long taskID, int *commandBuffer); + void doCommand(unsigned long taskID, uint8_t *commandBuffer, int byteCount); }; @@ -309,13 +309,22 @@ class RSproto : public IODevice { if (fail) _deviceState = DEVSTATE_FAILED; } - + int byteCounter = 0; public: +bool flagEnd = false; +bool flagEnded = false; +bool flagStart = false; +bool flagStarted = false; +bool rxStart = false; +bool rxEnd = false; +bool crcPass = false; +uint16_t received_crc; +uint8_t crc[2]; uint16_t crc16(uint8_t *data, uint16_t length); void remove_nulls(char *str, int len); int getCharsLeft(char *str, char position); - void parseRx(int * outArray); - void sendInstantCommand(int *buf); + void parseRx(uint8_t * outArray); + void sendInstantCommand(uint8_t *buf, int byteCount); // EX-IOExpander protocol flags enum { EXIOINIT = 0xE0, // Flag to initialise setup procedure From d892c27f3f3f956347a06220c23fc0a89e978053 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sat, 21 Dec 2024 11:42:24 -0500 Subject: [PATCH 62/73] saving current --- IO_RSproto.cpp | 403 +++++++++++++++++++++++++++++++++++-------------- IO_RSproto.h | 15 +- 2 files changed, 296 insertions(+), 122 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index a40a4215..8f06ae0c 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -27,10 +27,11 @@ static const byte PAYLOAD_STRING = 2; taskBuffer * taskBuffer::first=NULL; -taskBuffer::taskBuffer(unsigned long taskID, uint8_t *commandBuffer, int byteCount) +taskBuffer::taskBuffer(unsigned long taskID, uint8_t *commandBuffer, int byteCount, uint8_t retFlag) { _taskID = taskID; _byteCount = byteCount; + _retFlag = retFlag; memset(commandArray, 0, byteCount); memcpy(commandArray, commandBuffer, byteCount); @@ -78,16 +79,18 @@ int RSproto::getCharsLeft(char *str, char position) { else return 0; } -void taskBuffer::doCommand(unsigned long taskID, uint8_t *commandBuffer, int byteCount) { +void taskBuffer::doCommand(unsigned long taskID, uint8_t *commandBuffer, int byteCount, uint8_t retFlag) { // add commands here to be sent - new taskBuffer(taskID, commandBuffer, byteCount); + new taskBuffer(taskID, commandBuffer, byteCount, retFlag); } -void RSproto::parseRx(uint8_t * outArray) { +void RSproto::parseRx(uint8_t * outArray, uint8_t retFlag) { + int nodeTo = outArray[0]; int nodeFr = outArray[1]; int AddrCode = outArray[2]; - DIAG(F("From: %i, To: %i"), nodeFr,outArray[0]); + DIAG(F("From: %i, To: %i | %i %i %i %i %i"), nodeFr,nodeTo, outArray[3], outArray[4], outArray[5], outArray[6],outArray[7]); + return; RSprotonode *node = findNode(nodeFr); switch (AddrCode) { case EXIOPINS: @@ -135,42 +138,42 @@ void RSproto::parseRx(uint8_t * outArray) { } } } - node->resFlag = 1; + node->resFlag[retFlag] = 1; break;} case EXIOINITA: { for (int i = 0; i < node->_numAnaloguePins; i++) { node->_analoguePinMap[i] = outArray[i+3]; } - node->resFlag = 1; + node->resFlag[retFlag] = 1; break; } case EXIOVER: { node->_majorVer = outArray[3]; node->_minorVer = outArray[4]; node->_patchVer = outArray[5]; - node->resFlag = 1; + node->resFlag[retFlag] = 1; break; } case EXIORDY: { - node->resFlag = 1; + node->resFlag[retFlag] = 1; break; } case EXIOERR: { - node->resFlag = -1; + node->resFlag[retFlag] = -1; break; } case EXIORDD: { for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { node->_digitalInputStates[i] = outArray[i+3]; } - node->resFlag = 1; + node->resFlag[retFlag] = 1; break; } case EXIORDAN: { for (int i = 0; i < node->_numAnaloguePins; i++) { node->_analogueInputBuffer[i] = outArray[i+3]; } - node->resFlag = 1; + node->resFlag[retFlag] = 1; break; } } @@ -221,7 +224,7 @@ uint16_t RSproto::crc16(uint8_t *data, uint16_t length) { return crc; } -void RSproto::sendInstantCommand(uint8_t *buf, int byteCount) { +void RSproto::sendInstantCommand(uint8_t *buf, int byteCount, uint8_t retFlag) { // Calculate CRC for response data uint16_t response_crc = crc16((uint8_t*)buf, byteCount-1); if (_txPin != -1) digitalWrite(_txPin,HIGH); @@ -239,68 +242,167 @@ void RSproto::sendInstantCommand(uint8_t *buf, int byteCount) { _serial->write(0xFD); _serial->flush(); if (_txPin != -1) digitalWrite(_txPin,LOW); - // delete task command after sending, for now + bool flagProc = false; +uint8_t Ireceived_data[ARRAY_SIZE]; //int received_data[ARRAY_SIZE]; uint16_t received_crc; - while(_serial->available()) { - if (_serial->available()) { - uint8_t received_data[ARRAY_SIZE]; - - uint16_t calculated_crc; - int byteCount = 100; - - int curByte = _serial->read(); + int byteCounted = 0; + unsigned long startMillis = millis(); + while (!flagEnded) { + if (millis() - startMillis > 500) return; // safeguard timeout + while(_serial->available()) { + if (_serial->available()) { + - if (curByte == 0xFE && flagStart == false) flagStart = true; - else if ( curByte == 0xFE && flagStart == true) { - byteCounter = 0; - flagStarted = true; - flagStart = false; - flagEnded = false; - rxStart = true; - rxEnd = false; - crcPass = false; - }else if (flagStarted) { - crc[0] = curByte; - byteCounter++; - flagStarted = false; - } else if (byteCounter == 1) { - crc[1] = curByte; - received_crc = (crc[0] << 8) | crc[1]; - byteCounter++; - } else if (byteCounter == 2) { - byteCount = curByte; - byteCounter++; - } else if (flagEnded == false && byteCounter >= 3) { - received_data[byteCounter-3] = curByte; - byteCounter++; + uint16_t calculated_crc; + int byteCountRx = 100; + + int curByte = _serial->read(); + + if (curByte == 0xFE && flagStart == false) flagStart = true; + else if ( curByte == 0xFE && flagStart == true) { + byteCounter = 0; + flagStarted = true; + flagStart = false; + flagEnded = false; + rxStart = true; + rxEnd = false; + crcPass = false; + }else if (flagStarted) { + crc[0] = curByte; + byteCounter++; + flagStarted = false; + } else if (byteCounter == 1) { + crc[1] = curByte; + received_crc = (crc[0] << 8) | crc[1]; + byteCounter++; + } else if (byteCounter == 2) { + byteCountRx = curByte; + byteCounter++; + } else if (flagEnded == false && byteCounter >= 3) { + Ireceived_data[byteCounter-3] = curByte; + byteCounter++; + } + if (curByte == 0xFD && flagEnd == false) flagEnd = true; + else if ( curByte == 0xFD && flagEnd == true) { + flagEnded = true; + flagEnd = false; + rxEnd = true; + byteCounted = byteCounter; + byteCounter = 0; + calculated_crc = crc16((uint8_t*)Ireceived_data, byteCounted-6); + if (received_crc == calculated_crc) { + crcPass = true; + DIAG(F("CRC PASS %x %x BC: %i"), received_crc, calculated_crc, byteCounted); + } else { + DIAG(F("CRC FAIL %x %x BC: %i"), received_crc, calculated_crc, byteCounted); + } + return; + } + // Check CRC validity + if (crcPass) { + // Data received successfully, process it (e.g., print) + /*if (received_data[0] == 0) */flagProc = true; + //else DIAG(F("To Node: %i"), received_data[0]); + + } else { + //DIAG(F("IO_RSproto: CRC Error!")); + } } - if (curByte == 0xFD && flagEnd == false) flagEnd = true; - else if ( curByte == 0xFD && flagEnd == true) { - flagEnded = true; - flagEnd = false; - rxEnd = true; - byteCount = byteCounter; - byteCounter = 0; + } + } + if (flagProc) { + flagProc = false; + int nodeTo = Ireceived_data[0]; + int nodeFr = Ireceived_data[1]; + int AddrCode = Ireceived_data[2]; + DIAG(F("From: %i, To: %i | %i %i %i %i %i"), nodeFr,nodeTo, Ireceived_data[3], Ireceived_data[4], Ireceived_data[5], Ireceived_data[6],Ireceived_data[7]); + return; + RSprotonode *node = findNode(nodeFr); + switch (AddrCode) { + case EXIOPINS: + {node->_numDigitalPins = Ireceived_data[3]; + node->_numAnaloguePins = Ireceived_data[4]; + + // See if we already have suitable buffers assigned + if (node->_numDigitalPins>0) { + size_t digitalBytesNeeded = (node->_numDigitalPins + 7) / 8; + if (node->_digitalPinBytes < digitalBytesNeeded) { + // Not enough space, free any existing buffer and allocate a new one + if (node->_digitalPinBytes > 0) free(node->_digitalInputStates); + if ((node->_digitalInputStates = (byte*) calloc(digitalBytesNeeded, 1)) != NULL) { + node->_digitalPinBytes = digitalBytesNeeded; + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), nodeFr, digitalBytesNeeded); + //_deviceState = DEVSTATE_FAILED; + node->_digitalPinBytes = 0; + return; + } + } + } + + if (node->_numAnaloguePins>0) { + size_t analogueBytesNeeded = node->_numAnaloguePins * 2; + if (node->_analoguePinBytes < analogueBytesNeeded) { + // Free any existing buffers and allocate new ones. + if (node->_analoguePinBytes > 0) { + free(node->_analogueInputBuffer); + free(node->_analogueInputStates); + free(node->_analoguePinMap); + } + node->_analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1); + node->_analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1); + node->_analoguePinMap = (uint8_t*) calloc(node->_numAnaloguePins, 1); + if (node->_analogueInputStates != NULL && + node->_analogueInputBuffer != NULL && + node->_analoguePinMap != NULL) { + node->_analoguePinBytes = analogueBytesNeeded; + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), nodeFr); + //_deviceState = DEVSTATE_FAILED; + node->_analoguePinBytes = 0; + return; + } + } + } + node->resFlag[retFlag] = 1; + break;} + case EXIOINITA: { + for (int i = 0; i < node->_numAnaloguePins; i++) { + node->_analoguePinMap[i] = Ireceived_data[i+3]; + } + node->resFlag[retFlag] = 1; + break; + } + case EXIOVER: { + node->_majorVer = Ireceived_data[3]; + node->_minorVer = Ireceived_data[4]; + node->_patchVer = Ireceived_data[5]; + DIAG(F("EX-IOExpander device found, Node:%d, Version v%d.%d.%d"), node->getNodeID(), node->_majorVer, node->_minorVer, node->_patchVer); + break; + } + case EXIORDY: { + node->resFlag[retFlag] = 1; + break; } - if (flagEnded) { - calculated_crc = crc16((uint8_t*)received_data, byteCount-6); - if (received_crc == calculated_crc) { - crcPass = true; - DIAG(F("CRC PASS")); + case EXIOERR: { + node->resFlag[retFlag] = -1; + break; + } + case EXIORDD: { + for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { + node->_digitalInputStates[i] = Ireceived_data[i+3]; } - flagEnded = false; + node->resFlag[retFlag] = 1; + break; } - // Check CRC validity - if (crcPass) { - // Data received successfully, process it (e.g., print) - int nodeTo = (received_data[1] << 8) | received_data[0]; - if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. - parseRx(received_data); + case EXIORDAN: { + for (int i = 0; i < node->_numAnaloguePins; i++) { + node->_analogueInputBuffer[i] = Ireceived_data[i+3]; } - } else { - //DIAG(F("IO_RSproto: CRC Error!")); + node->resFlag[retFlag] = 1; + break; } } } @@ -341,7 +443,7 @@ void RSproto::_loop(unsigned long currentMicros) { } if (_serial->available()) { - uint8_t received_data[ARRAY_SIZE]; + uint16_t calculated_crc; int byteCount = 100; @@ -351,6 +453,7 @@ void RSproto::_loop(unsigned long currentMicros) { if (curByte == 0xFE && flagStart == false) flagStart = true; else if ( curByte == 0xFE && flagStart == true) { + flagProc = false; byteCounter = 0; flagStarted = true; flagStart = false; @@ -384,9 +487,9 @@ void RSproto::_loop(unsigned long currentMicros) { if (flagEnded) { calculated_crc = crc16((uint8_t*)received_data, byteCount-6); if (received_crc == calculated_crc) { - DIAG(F("CRC PASS")); + DIAG(F("Loop CRC PASS")); crcPass = true; - }else DIAG(F("CRC Fail %x %x"),received_crc,calculated_crc); + }else DIAG(F("Loop CRC Fail %x %x"),received_crc,calculated_crc); flagEnded = false; } // Check CRC validity @@ -394,14 +497,111 @@ void RSproto::_loop(unsigned long currentMicros) { // Data received successfully, process it (e.g., print) int nodeTo = received_data[0]; if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. - parseRx(received_data); + flagProc = true; + } + } else { //DIAG(F("IO_RSproto: CRC Error!")); } + task->getNext(); } - - task->getNext(); + if (flagProc) { + flagProc = false; + int nodeTo = received_data[0]; + int nodeFr = received_data[1]; + int AddrCode = received_data[2]; + DIAG(F("From: %i, To: %i | %i %i %i %i %i"), nodeFr,nodeTo, received_data[3], received_data[4], received_data[5], received_data[6],received_data[7]); + return; + RSprotonode *node = findNode(nodeFr); + switch (AddrCode) { + case EXIOPINS: + {node->_numDigitalPins = received_data[3]; + node->_numAnaloguePins = received_data[4]; + + // See if we already have suitable buffers assigned + if (node->_numDigitalPins>0) { + size_t digitalBytesNeeded = (node->_numDigitalPins + 7) / 8; + if (node->_digitalPinBytes < digitalBytesNeeded) { + // Not enough space, free any existing buffer and allocate a new one + if (node->_digitalPinBytes > 0) free(node->_digitalInputStates); + if ((node->_digitalInputStates = (byte*) calloc(digitalBytesNeeded, 1)) != NULL) { + node->_digitalPinBytes = digitalBytesNeeded; + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), nodeFr, digitalBytesNeeded); + //_deviceState = DEVSTATE_FAILED; + node->_digitalPinBytes = 0; + return; + } + } + } + + if (node->_numAnaloguePins>0) { + size_t analogueBytesNeeded = node->_numAnaloguePins * 2; + if (node->_analoguePinBytes < analogueBytesNeeded) { + // Free any existing buffers and allocate new ones. + if (node->_analoguePinBytes > 0) { + free(node->_analogueInputBuffer); + free(node->_analogueInputStates); + free(node->_analoguePinMap); + } + node->_analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1); + node->_analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1); + node->_analoguePinMap = (uint8_t*) calloc(node->_numAnaloguePins, 1); + if (node->_analogueInputStates != NULL && + node->_analogueInputBuffer != NULL && + node->_analoguePinMap != NULL) { + node->_analoguePinBytes = analogueBytesNeeded; + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), nodeFr); + //_deviceState = DEVSTATE_FAILED; + node->_analoguePinBytes = 0; + return; + } + } + } + node->resFlag[_currentTask->_retFlag] = 1; + break;} + case EXIOINITA: { + for (int i = 0; i < node->_numAnaloguePins; i++) { + node->_analoguePinMap[i] = received_data[i+3]; + } + node->resFlag[_currentTask->_retFlag] = 1; + break; + } + case EXIOVER: { + node->_majorVer = received_data[3]; + node->_minorVer = received_data[4]; + node->_patchVer = received_data[5]; + node->resFlag[_currentTask->_retFlag] = 1; + break; + } + case EXIORDY: { + node->resFlag[_currentTask->_retFlag] = 1; + break; + } + case EXIOERR: { + node->resFlag[_currentTask->_retFlag] = -1; + break; + } + case EXIORDD: { + for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { + node->_digitalInputStates[i] = received_data[i+3]; + } + node->resFlag[_currentTask->_retFlag] = 1; + break; + } + case EXIORDAN: { + for (int i = 0; i < node->_numAnaloguePins; i++) { + node->_analogueInputBuffer[i] = received_data[i+3]; + } + node->resFlag[_currentTask->_retFlag] = 1; + break; + } + } + + } + } // Link to chain of RSproto instances, left over from RSproto template. @@ -421,6 +621,7 @@ RSprotonode::RSprotonode(VPIN firstVpin, int nPins, uint8_t nodeID) { _nPins = nPins; _busNo = 0; _nodeID = nodeID; + memset(resFlag, 0, 255); //bus = bus->findBus(0); //_serial = bus->_serialD; if (_nodeID > 252) _nodeID = 252; // cannot have a node with the frame flags @@ -452,14 +653,9 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); bus->_busy = true; - bus->sendInstantCommand(buff, 5); + bus->sendInstantCommand(buff, 5, EXIODPUP); bus->_busy = false; - while (resFlag == 0 && millis() - startMillis < 500); // blocking for now - if (resFlag != 1) { - DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); - return false; - } - resFlag = 0; + return true; } @@ -476,14 +672,9 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); bus->_busy = true; - bus->sendInstantCommand(buff, 6); + bus->sendInstantCommand(buff, 6, EXIOENAN); bus->_busy = false; - while (resFlag == 0 && millis() - startMillis < 500); // blocking for now - if (resFlag != 1) { - DIAG(F("EX-IOExpander485 Vpin %u cannot be used as a digital input pin"), pin); - return false; - } - resFlag = 0; + return true; } @@ -498,37 +689,25 @@ void RSprotonode::_begin() { unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); bus->_busy = true; - bus->sendInstantCommand(buff, 6); + bus->sendInstantCommand(buff, 6, EXIOINIT); bus->_busy = false; - while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now - if (resFlag != 1) { - DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOINIT"), _nodeID); - } - resFlag = 0; + buff[0] = (_nodeID); buff[1] = (0); buff[2] = (EXIOINITA); startMillis = millis(); bus->_busy = true; - bus->sendInstantCommand(buff,3); + bus->sendInstantCommand(buff,3, EXIOINITA); bus->_busy = false; - while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now - if (resFlag != 1) { - DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOINITA"), _nodeID); - } - resFlag = 0; + buff[0] = (_nodeID); buff[1] = (0); buff[2] = (EXIOVER); startMillis = millis(); bus->_busy = true; - bus->sendInstantCommand(buff,3); + bus->sendInstantCommand(buff,3, EXIOVER); bus->_busy = false; - while (resFlag == 0 && millis() - startMillis < 1000); // blocking for now - if (resFlag != 1) { - DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); - } else DIAG(F("EX-IOExpander device found, Node:%d, Version v%d.%d.%d"), _nodeID, _majorVer, _minorVer, _patchVer); - resFlag = 0; + #ifdef DIAG_IO @@ -554,12 +733,8 @@ void RSprotonode::_write(VPIN vpin, int value) { buff[4] = (value); unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); - task->doCommand(bus->taskCounter++, buff,5); - while (resFlag == 0 && millis() - startMillis < 500); // blocking for now - if (resFlag != 1) { - DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); - } - resFlag = 0; + task->doCommand(bus->taskCounter++, buff, 5, EXIOWRD); + } int RSprotonode::_readAnalogue(VPIN vpin) { @@ -589,10 +764,6 @@ void RSprotonode::_write(VPIN vpin, int value) { buff[8] = lowByte(duration); unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); - task->doCommand(bus->taskCounter++, buff,9); - while (resFlag == 0 && millis() - startMillis < 500); // blocking for now - if (resFlag != 1) { - DIAG(F("EX-IOExpander485 Node:%d ERROR EXIOVER"), _nodeID); - } - resFlag = 0; + task->doCommand(bus->taskCounter++, buff, 9, EXIOWRAN); + } \ No newline at end of file diff --git a/IO_RSproto.h b/IO_RSproto.h index 5c516e19..c4a99ac4 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -99,6 +99,7 @@ class taskBuffer int commandArray[ARRAY_SIZE]; int _byteCount = 0; + uint8_t _retFlag = 0; // EX-IOExpander protocol flags enum { EXIOINIT = 0xE0, // Flag to initialise setup procedure @@ -144,9 +145,9 @@ class taskBuffer end->setNext(newTask); //DIAG(F("RSproto: 260h nodeID:%d _nodeListStart:%d _nodeListEnd:%d"), newNode, _nodeListStart, _nodeListEnd); } - taskBuffer(unsigned long taskID, uint8_t *commandBuffer, int byteCount); + taskBuffer(unsigned long taskID, uint8_t *commandBuffer, int byteCount, uint8_t retFlag); ~taskBuffer(); - void doCommand(unsigned long taskID, uint8_t *commandBuffer, int byteCount); + void doCommand(unsigned long taskID, uint8_t *commandBuffer, int byteCount, uint8_t retFlag); }; @@ -210,7 +211,7 @@ class RSprotonode : public IODevice { 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; - int resFlag = 0; + int resFlag[255]; bool _initalized; static void create(VPIN firstVpin, int nPins, uint8_t nodeID) { if (checkNoOverlap(firstVpin, nPins)) new RSprotonode(firstVpin, nPins, nodeID); @@ -248,7 +249,7 @@ class RSprotonode : public IODevice { } void _display() override { - 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("")); + DIAG(F("EX-IOExpander485 node:%d Vpins %u-%u %S"), _nodeID, (int)_firstVpin, (int)_firstVpin+_nPins-1, _deviceState == DEVSTATE_FAILED ? F("OFFLINE") : F("")); } }; @@ -318,13 +319,15 @@ bool flagStarted = false; bool rxStart = false; bool rxEnd = false; bool crcPass = false; +bool flagProc = false; +uint8_t received_data[ARRAY_SIZE]; uint16_t received_crc; uint8_t crc[2]; uint16_t crc16(uint8_t *data, uint16_t length); void remove_nulls(char *str, int len); int getCharsLeft(char *str, char position); - void parseRx(uint8_t * outArray); - void sendInstantCommand(uint8_t *buf, int byteCount); + void parseRx(uint8_t * outArray, uint8_t retFlag); + void sendInstantCommand(uint8_t *buf, int byteCount, uint8_t retFlag); // EX-IOExpander protocol flags enum { EXIOINIT = 0xE0, // Flag to initialise setup procedure From c151b521147f350026da275996e42e0852e43cb8 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sun, 22 Dec 2024 05:20:25 -0500 Subject: [PATCH 63/73] save current --- IO_RSproto.cpp | 698 +++++++++++++------------------------------------ IO_RSproto.h | 234 ++++++++++------- 2 files changed, 319 insertions(+), 613 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index 8f06ae0c..f2123b7f 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -25,159 +25,6 @@ static const byte PAYLOAD_FALSE = 0; static const byte PAYLOAD_NORMAL = 1; static const byte PAYLOAD_STRING = 2; -taskBuffer * taskBuffer::first=NULL; - -taskBuffer::taskBuffer(unsigned long taskID, uint8_t *commandBuffer, int byteCount, uint8_t retFlag) -{ - _taskID = taskID; - _byteCount = byteCount; - _retFlag = retFlag; - memset(commandArray, 0, byteCount); - memcpy(commandArray, commandBuffer, byteCount); - - next=first; - first=this; - - RSproto *bus = RSproto::findBus(0); - if (bus != NULL) { - bus->addTask(this); - return; - } -} - -taskBuffer::~taskBuffer() -{ - // destructor -} - -void RSproto::remove_nulls(char *str, int len) { - int i, j = 0; - for (i = 0; i= 0 && pos < strlen(str)) { - for (int i = 0; i < strlen(str); i++) { - if (i < pos) result[i] = str[i]; - } - } - if (result != NULL) return atoi(result); - else return 0; -} - -void taskBuffer::doCommand(unsigned long taskID, uint8_t *commandBuffer, int byteCount, uint8_t retFlag) { - // add commands here to be sent - new taskBuffer(taskID, commandBuffer, byteCount, retFlag); - -} - -void RSproto::parseRx(uint8_t * outArray, uint8_t retFlag) { - int nodeTo = outArray[0]; - int nodeFr = outArray[1]; - int AddrCode = outArray[2]; - DIAG(F("From: %i, To: %i | %i %i %i %i %i"), nodeFr,nodeTo, outArray[3], outArray[4], outArray[5], outArray[6],outArray[7]); - return; - RSprotonode *node = findNode(nodeFr); - switch (AddrCode) { - case EXIOPINS: - {node->_numDigitalPins = outArray[3]; - node->_numAnaloguePins = outArray[4]; - - // See if we already have suitable buffers assigned - if (node->_numDigitalPins>0) { - size_t digitalBytesNeeded = (node->_numDigitalPins + 7) / 8; - if (node->_digitalPinBytes < digitalBytesNeeded) { - // Not enough space, free any existing buffer and allocate a new one - if (node->_digitalPinBytes > 0) free(node->_digitalInputStates); - if ((node->_digitalInputStates = (byte*) calloc(digitalBytesNeeded, 1)) != NULL) { - node->_digitalPinBytes = digitalBytesNeeded; - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), nodeFr, digitalBytesNeeded); - //_deviceState = DEVSTATE_FAILED; - node->_digitalPinBytes = 0; - return; - } - } - } - - if (node->_numAnaloguePins>0) { - size_t analogueBytesNeeded = node->_numAnaloguePins * 2; - if (node->_analoguePinBytes < analogueBytesNeeded) { - // Free any existing buffers and allocate new ones. - if (node->_analoguePinBytes > 0) { - free(node->_analogueInputBuffer); - free(node->_analogueInputStates); - free(node->_analoguePinMap); - } - node->_analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1); - node->_analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1); - node->_analoguePinMap = (uint8_t*) calloc(node->_numAnaloguePins, 1); - if (node->_analogueInputStates != NULL && - node->_analogueInputBuffer != NULL && - node->_analoguePinMap != NULL) { - node->_analoguePinBytes = analogueBytesNeeded; - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), nodeFr); - //_deviceState = DEVSTATE_FAILED; - node->_analoguePinBytes = 0; - return; - } - } - } - node->resFlag[retFlag] = 1; - break;} - case EXIOINITA: { - for (int i = 0; i < node->_numAnaloguePins; i++) { - node->_analoguePinMap[i] = outArray[i+3]; - } - node->resFlag[retFlag] = 1; - break; - } - case EXIOVER: { - node->_majorVer = outArray[3]; - node->_minorVer = outArray[4]; - node->_patchVer = outArray[5]; - node->resFlag[retFlag] = 1; - break; - } - case EXIORDY: { - node->resFlag[retFlag] = 1; - break; - } - case EXIOERR: { - node->resFlag[retFlag] = -1; - break; - } - case EXIORDD: { - for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { - node->_digitalInputStates[i] = outArray[i+3]; - } - node->resFlag[retFlag] = 1; - break; - } - case EXIORDAN: { - for (int i = 0; i < node->_numAnaloguePins; i++) { - node->_analogueInputBuffer[i] = outArray[i+3]; - } - node->resFlag[retFlag] = 1; - break; - } - } -} /************************************************************ * RSproto implementation @@ -224,384 +71,203 @@ uint16_t RSproto::crc16(uint8_t *data, uint16_t length) { return crc; } -void RSproto::sendInstantCommand(uint8_t *buf, int byteCount, uint8_t retFlag) { - // Calculate CRC for response data - uint16_t response_crc = crc16((uint8_t*)buf, byteCount-1); - if (_txPin != -1) digitalWrite(_txPin,HIGH); - // Send response data with CRC - _serial->write(0xFE); - _serial->write(0xFE); - _serial->write(response_crc >> 8); - _serial->write(response_crc & 0xFF); - _serial->write(byteCount); - for (int i = 0; i < byteCount; i++) { - _serial->write(buf[i]); - - } - _serial->write(0xFD); - _serial->write(0xFD); - _serial->flush(); - if (_txPin != -1) digitalWrite(_txPin,LOW); - - bool flagProc = false; -uint8_t Ireceived_data[ARRAY_SIZE]; - //int received_data[ARRAY_SIZE]; - uint16_t received_crc; - int byteCounted = 0; - unsigned long startMillis = millis(); - while (!flagEnded) { - if (millis() - startMillis > 500) return; // safeguard timeout - while(_serial->available()) { - if (_serial->available()) { - - - uint16_t calculated_crc; - int byteCountRx = 100; - - int curByte = _serial->read(); - - if (curByte == 0xFE && flagStart == false) flagStart = true; - else if ( curByte == 0xFE && flagStart == true) { - byteCounter = 0; - flagStarted = true; - flagStart = false; - flagEnded = false; - rxStart = true; - rxEnd = false; - crcPass = false; - }else if (flagStarted) { - crc[0] = curByte; - byteCounter++; - flagStarted = false; - } else if (byteCounter == 1) { - crc[1] = curByte; - received_crc = (crc[0] << 8) | crc[1]; - byteCounter++; - } else if (byteCounter == 2) { - byteCountRx = curByte; - byteCounter++; - } else if (flagEnded == false && byteCounter >= 3) { - Ireceived_data[byteCounter-3] = curByte; - byteCounter++; - } - if (curByte == 0xFD && flagEnd == false) flagEnd = true; - else if ( curByte == 0xFD && flagEnd == true) { - flagEnded = true; - flagEnd = false; - rxEnd = true; - byteCounted = byteCounter; - byteCounter = 0; - calculated_crc = crc16((uint8_t*)Ireceived_data, byteCounted-6); - if (received_crc == calculated_crc) { - crcPass = true; - DIAG(F("CRC PASS %x %x BC: %i"), received_crc, calculated_crc, byteCounted); - } else { - DIAG(F("CRC FAIL %x %x BC: %i"), received_crc, calculated_crc, byteCounted); - } - return; - } - // Check CRC validity - if (crcPass) { - // Data received successfully, process it (e.g., print) - /*if (received_data[0] == 0) */flagProc = true; - //else DIAG(F("To Node: %i"), received_data[0]); - - } else { - //DIAG(F("IO_RSproto: CRC Error!")); - } +void RSproto::_loop(unsigned long currentMicros) { + _currentMicros = currentMicros; + //if (_busy == true) return; + if (_currentMicros - _cycleStartTime < _cycleTime) return; + _cycleStartTime = _currentMicros; + + Task currentTask = getNextTask(); + if (currentTask.completed != true) { // Check if a task was found + + // Calculate CRC for response data + if (!currentTask.rxMode) { + currentTask.crcPassFail = 0; + uint16_t response_crc = crc16((uint8_t*)currentTask.commandArray, currentTask.byteCount-1); + if (_txPin != -1) digitalWrite(_txPin,HIGH); + // Send response data with CRC + _serial->write(0xFE); + _serial->write(0xFE); + _serial->write(response_crc >> 8); + _serial->write(response_crc & 0xFF); + _serial->write(currentTask.byteCount); + for (int i = 0; i < currentTask.byteCount; i++) { + _serial->write(currentTask.commandArray[i]); } + _serial->write(0xFD); + _serial->write(0xFD); + _serial->flush(); + if (_txPin != -1) digitalWrite(_txPin,LOW); + // delete task command after sending, for now + markTaskCompleted(currentTask.taskID); } - } - if (flagProc) { - flagProc = false; - int nodeTo = Ireceived_data[0]; - int nodeFr = Ireceived_data[1]; - int AddrCode = Ireceived_data[2]; - DIAG(F("From: %i, To: %i | %i %i %i %i %i"), nodeFr,nodeTo, Ireceived_data[3], Ireceived_data[4], Ireceived_data[5], Ireceived_data[6],Ireceived_data[7]); - return; - RSprotonode *node = findNode(nodeFr); - switch (AddrCode) { - case EXIOPINS: - {node->_numDigitalPins = Ireceived_data[3]; - node->_numAnaloguePins = Ireceived_data[4]; - // See if we already have suitable buffers assigned - if (node->_numDigitalPins>0) { - size_t digitalBytesNeeded = (node->_numDigitalPins + 7) / 8; - if (node->_digitalPinBytes < digitalBytesNeeded) { - // Not enough space, free any existing buffer and allocate a new one - if (node->_digitalPinBytes > 0) free(node->_digitalInputStates); - if ((node->_digitalInputStates = (byte*) calloc(digitalBytesNeeded, 1)) != NULL) { - node->_digitalPinBytes = digitalBytesNeeded; - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), nodeFr, digitalBytesNeeded); - //_deviceState = DEVSTATE_FAILED; - node->_digitalPinBytes = 0; - return; - } - } - } - - if (node->_numAnaloguePins>0) { - size_t analogueBytesNeeded = node->_numAnaloguePins * 2; - if (node->_analoguePinBytes < analogueBytesNeeded) { - // Free any existing buffers and allocate new ones. - if (node->_analoguePinBytes > 0) { - free(node->_analogueInputBuffer); - free(node->_analogueInputStates); - free(node->_analoguePinMap); - } - node->_analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1); - node->_analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1); - node->_analoguePinMap = (uint8_t*) calloc(node->_numAnaloguePins, 1); - if (node->_analogueInputStates != NULL && - node->_analogueInputBuffer != NULL && - node->_analoguePinMap != NULL) { - node->_analoguePinBytes = analogueBytesNeeded; - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), nodeFr); - //_deviceState = DEVSTATE_FAILED; - node->_analoguePinBytes = 0; - return; - } - } - } - node->resFlag[retFlag] = 1; - break;} - case EXIOINITA: { - for (int i = 0; i < node->_numAnaloguePins; i++) { - node->_analoguePinMap[i] = Ireceived_data[i+3]; - } - node->resFlag[retFlag] = 1; - break; - } - case EXIOVER: { - node->_majorVer = Ireceived_data[3]; - node->_minorVer = Ireceived_data[4]; - node->_patchVer = Ireceived_data[5]; - DIAG(F("EX-IOExpander device found, Node:%d, Version v%d.%d.%d"), node->getNodeID(), node->_majorVer, node->_minorVer, node->_patchVer); - break; - } - case EXIORDY: { - node->resFlag[retFlag] = 1; - break; + if (_serial->available() && currentTask.rxMode) { + + + uint16_t calculated_crc; + int byteCount = 100; + + uint8_t byte_array[byteCount]; + int curByte = _serial->read(); + + if (curByte == 0xFE && flagStart == false) flagStart = true; + else if ( curByte == 0xFE && flagStart == true) { + flagProc = false; + byteCounter = 0; + flagStarted = true; + flagStart = false; + flagEnded = false; + rxStart = true; + rxEnd = false; + crcPass = false; + }else if (flagStarted) { + crc[0] = curByte; + byteCounter++; + flagStarted = false; + } else if (byteCounter == 1) { + crc[1] = curByte; + received_crc = (crc[0] << 8) | crc[1]; + byteCounter++; + } else if (byteCounter == 2) { + byteCount = curByte; + byteCounter++; + } else if (flagEnded == false && byteCounter >= 3) { + received_data[byteCounter-3] = curByte; + byteCounter++; } - case EXIOERR: { - node->resFlag[retFlag] = -1; - break; + if (curByte == 0xFD && flagEnd == false) flagEnd = true; + else if ( curByte == 0xFD && flagEnd == true) { + flagEnded = true; + flagEnd = false; + rxEnd = true; + byteCount = byteCounter; + byteCounter = 0; } - case EXIORDD: { - for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { - node->_digitalInputStates[i] = Ireceived_data[i+3]; - } - node->resFlag[retFlag] = 1; - break; + if (flagEnded) { + calculated_crc = crc16((uint8_t*)received_data, byteCount-7); + if (received_crc == calculated_crc) { + DIAG(F("Loop CRC PASS")); + crcPass = true; + currentTask.crcPassFail = 1; + }else { + DIAG(F("Loop CRC Fail %x %x"),received_crc,calculated_crc); + currentTask.crcPassFail = -1; + } + flagEnded = false; + + } - case EXIORDAN: { - for (int i = 0; i < node->_numAnaloguePins; i++) { - node->_analogueInputBuffer[i] = Ireceived_data[i+3]; + // Check CRC validity + if (crcPass) { + // Data received successfully, process it (e.g., print) + int nodeTo = received_data[0]; + if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. + flagProc = true; + currentTask.gotCallback = true; + } - node->resFlag[retFlag] = 1; - break; + + } else { + //DIAG(F("IO_RSproto: CRC Error!")); } } - } -} - -void RSproto::_loop(unsigned long currentMicros) { - _currentMicros = currentMicros; - if (_busy == true) return; - if (_currentTask == NULL) { - _currentTask = _taskListStart; - - } - if (_currentMicros - _cycleStartTime < _cycleTime) return; - _cycleStartTime = _currentMicros; - if (_currentTask != NULL && _currentTask->_byteCount > 0) { + // temp debug - // Calculate CRC for response data - - uint16_t response_crc = crc16((uint8_t*)_currentTask->commandArray, _currentTask->_byteCount-6); - if (_txPin != -1) digitalWrite(_txPin,HIGH); - // Send response data with CRC - _serial->write(0xFE); - _serial->write(0xFE); - _serial->write(response_crc >> 8); - _serial->write(response_crc & 0xFF); - _serial->write(_currentTask->_byteCount); - for (int i = 0; i < _currentTask->_byteCount; i++) { - _serial->write(_currentTask->commandArray[i]); - } - _serial->write(0xFD); - _serial->write(0xFD); - _serial->flush(); - if (_txPin != -1) digitalWrite(_txPin,LOW); - // delete task command after sending, for now - memset(_currentTask->commandArray, 0, _currentTask->_byteCount); - _currentTask->_byteCount = 0; - } + //end debug - if (_serial->available()) { - - - uint16_t calculated_crc; - int byteCount = 100; - - uint8_t byte_array[byteCount]; - int curByte = _serial->read(); - - if (curByte == 0xFE && flagStart == false) flagStart = true; - else if ( curByte == 0xFE && flagStart == true) { - flagProc = false; - byteCounter = 0; - flagStarted = true; - flagStart = false; - flagEnded = false; - rxStart = true; - rxEnd = false; - crcPass = false; - }else if (flagStarted) { - crc[0] = curByte; - byteCounter++; - flagStarted = false; - } else if (byteCounter == 1) { - crc[1] = curByte; - received_crc = (crc[0] << 8) | crc[1]; - byteCounter++; - } else if (byteCounter == 2) { - byteCount = curByte; - byteCounter++; - } else if (flagEnded == false && byteCounter >= 3) { - received_data[byteCounter-3] = curByte; - byteCounter++; - } - if (curByte == 0xFD && flagEnd == false) flagEnd = true; - else if ( curByte == 0xFD && flagEnd == true) { - flagEnded = true; - flagEnd = false; - rxEnd = true; - byteCount = byteCounter; - byteCounter = 0; - } - if (flagEnded) { - calculated_crc = crc16((uint8_t*)received_data, byteCount-6); - if (received_crc == calculated_crc) { - DIAG(F("Loop CRC PASS")); - crcPass = true; - }else DIAG(F("Loop CRC Fail %x %x"),received_crc,calculated_crc); - flagEnded = false; - } - // Check CRC validity - if (crcPass) { - // Data received successfully, process it (e.g., print) + if (flagProc) { int nodeTo = received_data[0]; - if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. - flagProc = true; - - } - - } else { - //DIAG(F("IO_RSproto: CRC Error!")); - } - task->getNext(); - } - if (flagProc) { - flagProc = false; - int nodeTo = received_data[0]; - int nodeFr = received_data[1]; - int AddrCode = received_data[2]; - DIAG(F("From: %i, To: %i | %i %i %i %i %i"), nodeFr,nodeTo, received_data[3], received_data[4], received_data[5], received_data[6],received_data[7]); - return; - RSprotonode *node = findNode(nodeFr); - switch (AddrCode) { - case EXIOPINS: - {node->_numDigitalPins = received_data[3]; - node->_numAnaloguePins = received_data[4]; - - // See if we already have suitable buffers assigned - if (node->_numDigitalPins>0) { - size_t digitalBytesNeeded = (node->_numDigitalPins + 7) / 8; - if (node->_digitalPinBytes < digitalBytesNeeded) { - // Not enough space, free any existing buffer and allocate a new one - if (node->_digitalPinBytes > 0) free(node->_digitalInputStates); - if ((node->_digitalInputStates = (byte*) calloc(digitalBytesNeeded, 1)) != NULL) { - node->_digitalPinBytes = digitalBytesNeeded; - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), nodeFr, digitalBytesNeeded); - //_deviceState = DEVSTATE_FAILED; - node->_digitalPinBytes = 0; - return; + int nodeFr = received_data[1]; + int AddrCode = received_data[2]; + //DIAG(F("From: %i, To: %i | %i %i %i %i %i"), nodeFr,nodeTo, received_data[3], received_data[4], received_data[5], received_data[6],received_data[7]); + //return; + RSprotonode *node = findNode(nodeFr); + switch (AddrCode) { + case EXIOPINS: + {node->setnumDigitalPins(received_data[3]); + node->setnumAnalogPins(received_data[4]); + + // See if we already have suitable buffers assigned + if (node->getnumDigialPins()>0) { + size_t digitalBytesNeeded = (node->getnumDigialPins() + 7) / 8; + if (node->getdigitalPinBytes() < digitalBytesNeeded) { + // Not enough space, free any existing buffer and allocate a new one + if (node->cleandigitalPinStates(digitalBytesNeeded)) { + node->setdigitalPinBytes(digitalBytesNeeded); + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), nodeFr, digitalBytesNeeded); + //_deviceState = DEVSTATE_FAILED; + node->setdigitalPinBytes(0); + return; + } } } - } - - if (node->_numAnaloguePins>0) { - size_t analogueBytesNeeded = node->_numAnaloguePins * 2; - if (node->_analoguePinBytes < analogueBytesNeeded) { - // Free any existing buffers and allocate new ones. - if (node->_analoguePinBytes > 0) { - free(node->_analogueInputBuffer); - free(node->_analogueInputStates); - free(node->_analoguePinMap); - } - node->_analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1); - node->_analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1); - node->_analoguePinMap = (uint8_t*) calloc(node->_numAnaloguePins, 1); - if (node->_analogueInputStates != NULL && - node->_analogueInputBuffer != NULL && - node->_analoguePinMap != NULL) { - node->_analoguePinBytes = analogueBytesNeeded; - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), nodeFr); - //_deviceState = DEVSTATE_FAILED; - node->_analoguePinBytes = 0; - return; + + if (node->getnumAnalogPins()>0) { + size_t analogueBytesNeeded = node->getnumAnalogPins() * 2; + if (node->getanalogPinBytes() < analogueBytesNeeded) { + // Free any existing buffers and allocate new ones. + + if (node->cleanAnalogStates(analogueBytesNeeded)) { + node->setanalogPinBytes(analogueBytesNeeded); + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), nodeFr); + //_deviceState = DEVSTATE_FAILED; + node->setanalogPinBytes(0); + return; + } } } + node->resFlag[currentTask.retFlag] = 1; + break;} + case EXIOINITA: { + for (int i = 0; i < node->getnumAnalogPins(); i++) { + node->setanalogPinMap(received_data[i+3], i); + } + node->resFlag[currentTask.retFlag] = 1; + break; } - node->resFlag[_currentTask->_retFlag] = 1; - break;} - case EXIOINITA: { - for (int i = 0; i < node->_numAnaloguePins; i++) { - node->_analoguePinMap[i] = received_data[i+3]; + case EXIOVER: { + node->setMajVer(received_data[3]); + node->setMinVer(received_data[4]); + node->setPatVer(received_data[5]); + DIAG(F("EX-IOExpander485: Found node %i v%i.%i.%i"),node->getNodeID(), node->getMajVer(), node->getMinVer(), node->getPatVer()); + node->resFlag[currentTask.retFlag] = 1; + break; } - node->resFlag[_currentTask->_retFlag] = 1; - break; - } - case EXIOVER: { - node->_majorVer = received_data[3]; - node->_minorVer = received_data[4]; - node->_patchVer = received_data[5]; - node->resFlag[_currentTask->_retFlag] = 1; - break; - } - case EXIORDY: { - node->resFlag[_currentTask->_retFlag] = 1; - break; - } - case EXIOERR: { - node->resFlag[_currentTask->_retFlag] = -1; - break; - } - case EXIORDD: { - for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { - node->_digitalInputStates[i] = received_data[i+3]; + case EXIORDY: { + node->resFlag[currentTask.retFlag] = 1; + break; } - node->resFlag[_currentTask->_retFlag] = 1; - break; - } - case EXIORDAN: { - for (int i = 0; i < node->_numAnaloguePins; i++) { - node->_analogueInputBuffer[i] = received_data[i+3]; + case EXIOERR: { + node->resFlag[currentTask.retFlag] = -1; + break; + } + case EXIORDD: { + for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { + node->setanalogInputStates(received_data[i+3], i); + } + node->resFlag[currentTask.retFlag] = 1; + break; + } + case EXIORDAN: { + for (int i = 0; i < node->_numAnaloguePins; i++) { + node->setanalogInputBuffer(received_data[i+3], i); + } + + node->resFlag[currentTask.retFlag] = 1; + break; } - node->resFlag[_currentTask->_retFlag] = 1; - break; } + } - + if (currentTask.gotCallback) { + + } + } - } // Link to chain of RSproto instances, left over from RSproto template. @@ -653,7 +319,7 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); bus->_busy = true; - bus->sendInstantCommand(buff, 5, EXIODPUP); + bus->addTask(bus->taskIDCntr++, buff, 5, EXIODPUP); bus->_busy = false; return true; @@ -672,7 +338,7 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); bus->_busy = true; - bus->sendInstantCommand(buff, 6, EXIOENAN); + bus->addTask(bus->taskIDCntr++, buff, 6, EXIOENAN); bus->_busy = false; return true; @@ -689,7 +355,7 @@ void RSprotonode::_begin() { unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); bus->_busy = true; - bus->sendInstantCommand(buff, 6, EXIOINIT); + bus->addTask(bus->taskIDCntr++, buff, 6, EXIOINIT); bus->_busy = false; buff[0] = (_nodeID); @@ -697,16 +363,16 @@ void RSprotonode::_begin() { buff[2] = (EXIOINITA); startMillis = millis(); bus->_busy = true; - bus->sendInstantCommand(buff,3, EXIOINITA); - bus->_busy = false; + bus->addTask(bus->taskIDCntr++, buff, 3, EXIOINITA); + bus->_busy = false; buff[0] = (_nodeID); buff[1] = (0); buff[2] = (EXIOVER); startMillis = millis(); bus->_busy = true; - bus->sendInstantCommand(buff,3, EXIOVER); - bus->_busy = false; + bus->addTask(bus->taskIDCntr++, buff, 3, EXIOVER); + bus->_busy = false; @@ -733,7 +399,7 @@ void RSprotonode::_write(VPIN vpin, int value) { buff[4] = (value); unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); - task->doCommand(bus->taskCounter++, buff, 5, EXIOWRD); + bus->addTask(bus->taskIDCntr++, buff, 5, EXIOWRD); } @@ -764,6 +430,6 @@ void RSprotonode::_write(VPIN vpin, int value) { buff[8] = lowByte(duration); unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); - task->doCommand(bus->taskCounter++, buff, 9, EXIOWRAN); + bus->addTask(bus->taskIDCntr++, buff, 9, EXIOWRAN); } \ No newline at end of file diff --git a/IO_RSproto.h b/IO_RSproto.h index c4a99ac4..c1404f33 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -76,81 +76,6 @@ class RSprotonode; * Data Packet must always precede the parameters with the Command byte. * this way the data is processed by the correct routine. **********************************************************************/ -class taskBuffer -{ -private: - HardwareSerial * hwSerial; - unsigned long _baud; - static const int ARRAY_SIZE = 254; -public: - static taskBuffer *first; - static taskBuffer *end; - RSprotonode *node; - unsigned long _taskID; - Stream * serial; - uint8_t _txPin; - uint8_t _nodeID; - uint8_t _commandType; - unsigned long _cycleTimer = 0ul; - - taskBuffer *next; - uint8_t startChar[1] = {0xFD}; - uint8_t endChar[1] = {0xFE}; - - int commandArray[ARRAY_SIZE]; - int _byteCount = 0; - uint8_t _retFlag = 0; - // EX-IOExpander protocol flags - enum { - EXIOINIT = 0xE0, // Flag to initialise setup procedure - EXIORDY = 0xE1, // Flag we have completed setup procedure, also for EX-IO to ACK setup - EXIODPUP = 0xE2, // Flag we're sending digital pin pullup configuration - EXIOVER = 0xE3, // Flag to get version - EXIORDAN = 0xE4, // Flag to read an analogue input - EXIOWRD = 0xE5, // Flag for digital write - EXIORDD = 0xE6, // Flag to read digital input - EXIOENAN = 0xE7, // Flag to enable an analogue pin - EXIOINITA = 0xE8, // Flag we're receiving analogue pin mappings - EXIOPINS = 0xE9, // Flag we're receiving pin counts for buffers - EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM) - EXIOERR = 0xEF, // Flag we've received an error - }; - // RSproto protocol frame bytes - enum { - STARTBYTE = 0xFD, - ENDBYTE = 0xFE, - }; - - - - unsigned long getTaskID() { - return _taskID; - } - void setTaskID(unsigned long taskID) { - _taskID = taskID; - } - taskBuffer *getNext() { - return next; - } - - void setNext(taskBuffer *task) { - next = task; - } - void addTask(taskBuffer *newTask) { - if (!first) - first = newTask; - if (!end) - end = newTask; - else - end->setNext(newTask); - //DIAG(F("RSproto: 260h nodeID:%d _nodeListStart:%d _nodeListEnd:%d"), newNode, _nodeListStart, _nodeListEnd); - } - taskBuffer(unsigned long taskID, uint8_t *commandBuffer, int byteCount, uint8_t retFlag); - ~taskBuffer(); - void doCommand(unsigned long taskID, uint8_t *commandBuffer, int byteCount, uint8_t retFlag); -}; - - @@ -170,7 +95,6 @@ class RSprotonode : public IODevice { RSprotonode *_next = NULL; bool _initialised = false; RSproto *bus; - taskBuffer *task; HardwareSerial* _serial; enum { EXIOINIT = 0xE0, // Flag to initialise setup procedure @@ -200,17 +124,103 @@ class RSprotonode : public IODevice { }; uint8_t _numDigitalPins = 0; + uint8_t getnumDigialPins() { + return _numDigitalPins; + } + void setnumDigitalPins(uint8_t value) { + _numDigitalPins = value; + } uint8_t _numAnaloguePins = 0; + uint8_t getnumAnalogPins() { + return _numAnaloguePins; + } + void setnumAnalogPins(uint8_t value) { + _numAnaloguePins = value; + } uint8_t _majorVer = 0; + uint8_t getMajVer() { + return _majorVer; + } + void setMajVer(uint8_t value) { + _majorVer = value; + } uint8_t _minorVer = 0; + uint8_t getMinVer() { + return _minorVer; + } + void setMinVer(uint8_t value) { + _minorVer = value; + } uint8_t _patchVer = 0; + uint8_t getPatVer() { + return _patchVer; + } + void setPatVer(uint8_t value) { + _patchVer = value; + } uint8_t* _digitalInputStates = NULL; + uint8_t getdigitalInputStates(int index) { + return _digitalInputStates[index]; + } + void setdigitalInputStates(uint8_t value, int index) { + _digitalInputStates[index] = value; + } + bool cleandigitalPinStates(int size) { + if (_digitalPinBytes > 0) free(_digitalInputStates); + if ((_digitalInputStates = (byte*) calloc(size, 1)) != NULL) { + return true; + } else return false; + } uint8_t* _analogueInputStates = NULL; + uint8_t getanalogInputStates(int index) { + return _analogueInputStates[index]; + } + void setanalogInputStates(uint8_t value, int index) { + _analogueInputStates[index] = value; + } + uint8_t* _analogueInputBuffer = NULL; // buffer for I2C input transfers - uint8_t _readCommandBuffer[4]; + uint8_t getanalogInpuBuffer(int index) { + return _analogueInputBuffer[index]; + } + void setanalogInputBuffer(uint8_t value, int index) { + _analogueInputBuffer[index] = value; + memcpy(_analogueInputStates, _analogueInputBuffer, _analoguePinBytes); + } + uint8_t _readCommandBuffer[4]; // unused? uint8_t _digitalPinBytes = 0; // Size of allocated memory buffer (may be longer than needed) + uint8_t getdigitalPinBytes() { + return _digitalPinBytes; + } + void setdigitalPinBytes(uint8_t value) { + _digitalPinBytes = value; + } uint8_t _analoguePinBytes = 0; // Size of allocated memory buffer (may be longer than needed) + uint8_t getanalogPinBytes() { + return _analoguePinBytes; + } + void setanalogPinBytes(uint8_t value) { + _analoguePinBytes = value; + } uint8_t* _analoguePinMap = NULL; + uint8_t getanalogPinMap(int index) { + return _analoguePinMap[index]; + } + void setanalogPinMap(uint8_t value, int index) { + _analoguePinMap[index] = value; + } + bool cleanAnalogStates(int size) { + if (_analoguePinBytes > 0) { + free(_analogueInputBuffer); + free(_analogueInputStates); + free(_analoguePinMap); + } + _analogueInputStates = (uint8_t*) calloc(size, 1); + _analogueInputBuffer = (uint8_t*) calloc(size, 1); + _analoguePinMap = (uint8_t*) calloc(_numAnaloguePins, 1); + if (_analogueInputStates != NULL && _analogueInputBuffer != NULL && _analoguePinMap != NULL) return true; + else return false; + } int resFlag[255]; bool _initalized; static void create(VPIN firstVpin, int nPins, uint8_t nodeID) { @@ -266,7 +276,6 @@ class RSproto : public IODevice { private: // Here we define the device-specific variables. uint8_t _busNo; - taskBuffer *task; unsigned long _cycleStartTime = 0; unsigned long _timeoutStart = 0; unsigned long _cycleTime; // target time between successive read/write cycles, microseconds @@ -300,7 +309,6 @@ class RSproto : public IODevice { RSprotonode *_nodeListStart = NULL, *_nodeListEnd = NULL; RSprotonode *_currentNode = NULL; - taskBuffer *_taskListStart = NULL, *_taskListEnd = NULL, *_currentTask=NULL; uint16_t _receiveDataIndex = 0; // Index of next data byte to be received. RSproto *_nextBus = NULL; // Pointer to next bus instance in list. @@ -312,6 +320,54 @@ class RSproto : public IODevice { } int byteCounter = 0; public: +struct Task { + static const int ARRAY_SIZE = 254; + int taskID; + uint8_t commandArray[ARRAY_SIZE]; + int byteCount; + uint8_t retFlag; + bool gotCallback; + bool rxMode; + int crcPassFail; + bool completed = false; + }; + int taskIDCntr = 0; +Task taskBuffer[100]; // Buffer to hold up to 100 tasks +int taskCount = 0; +void addTask(int id, const uint8_t* cmd, int byteCount, uint8_t retFlag, bool gotCallBack=false, bool rxMode=false, int crcPassFail=0) { + if (taskCount < 100) { // Check if buffer is not full + taskBuffer[taskCount].taskID = id; + memcpy(taskBuffer[taskCount].commandArray, cmd, ARRAY_SIZE); + taskBuffer[taskCount].commandArray[ARRAY_SIZE] = 0; // Ensure null-termination + taskBuffer[taskCount].byteCount = byteCount; + taskBuffer[taskCount].retFlag = retFlag; + taskBuffer[taskCount].gotCallback = false; + taskBuffer[taskCount].rxMode = false; + taskBuffer[taskCount].crcPassFail = 0; + taskBuffer[taskCount].completed = false; + taskCount++; + } else { + Serial.println("Task buffer overflow!"); + } +} +Task getNextTask() { + for (int i = 0; i < taskCount; i++) { + if (!taskBuffer[i].completed) { + return taskBuffer[i]; + } + } + // Return a default task if no uncompleted tasks found + Task emptyTask; + return emptyTask; +} +void markTaskCompleted(int taskID) { + for (int i = 0; i < taskCount; i++) { + if (taskBuffer[i].taskID == taskID) { + taskBuffer[i].completed = true; + break; + } + } +} bool flagEnd = false; bool flagEnded = false; bool flagStart = false; @@ -327,7 +383,6 @@ uint16_t crc16(uint8_t *data, uint16_t length); void remove_nulls(char *str, int len); int getCharsLeft(char *str, char position); void parseRx(uint8_t * outArray, uint8_t retFlag); - void sendInstantCommand(uint8_t *buf, int byteCount, uint8_t retFlag); // EX-IOExpander protocol flags enum { EXIOINIT = 0xE0, // Flag to initialise setup procedure @@ -356,7 +411,7 @@ uint16_t crc16(uint8_t *data, uint16_t length); unsigned long _baud; int taskCnt = 0; uint8_t initBuffer[1] = {0xFE}; - unsigned long taskCounter=0; + unsigned long taskCounter=0ul; // Device-specific initialisation void _begin() override { _serial->begin(_baud, SERIAL_8N1); @@ -400,13 +455,6 @@ uint16_t crc16(uint8_t *data, uint16_t length); } return NULL; } - taskBuffer *findTask(uint8_t taskID) { - for (taskBuffer *task = _taskListStart; task != NULL; task = task->getNext()) { - if (task->getTaskID() == taskID) - return task; - } - return NULL; - } bool nodesInitialized() { bool retval = true; @@ -426,15 +474,7 @@ uint16_t crc16(uint8_t *data, uint16_t length); _nodeListEnd->setNext(newNode); //DIAG(F("RSproto: 260h nodeID:%d _nodeListStart:%d _nodeListEnd:%d"), newNode, _nodeListStart, _nodeListEnd); } -void addTask(taskBuffer *newTask) { - if (!_taskListStart) - _taskListStart = newTask; - if (!_nodeListEnd) - _taskListEnd = newTask; - else - _taskListEnd->setNext(newTask); - //DIAG(F("RSproto: 260h nodeID:%d _nodeListStart:%d _nodeListEnd:%d"), newNode, _nodeListStart, _nodeListEnd); - } + protected: RSproto(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin, int cycleTime); From 250a34bc093de59da1f9ee9b01a59664c8b6949c Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sun, 22 Dec 2024 16:02:07 -0500 Subject: [PATCH 64/73] sorta works --- IO_RSproto.cpp | 391 +++++++++++++++++++++++++++---------------------- IO_RSproto.h | 108 ++++++++++---- 2 files changed, 291 insertions(+), 208 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index f2123b7f..f16beb96 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -37,7 +37,7 @@ RSproto::RSproto(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8 _txPin = txPin; _busNo = busNo; - _cycleTime = cycleTime; + _cycleTime = cycleTime * 1000UL; bufferLength=0; inCommandPayload=PAYLOAD_FALSE; // Add device to HAL device chain @@ -74,199 +74,233 @@ uint16_t RSproto::crc16(uint8_t *data, uint16_t length) { void RSproto::_loop(unsigned long currentMicros) { _currentMicros = currentMicros; //if (_busy == true) return; - if (_currentMicros - _cycleStartTime < _cycleTime) return; - _cycleStartTime = _currentMicros; + - Task currentTask = getNextTask(); - if (currentTask.completed != true) { // Check if a task was found - - // Calculate CRC for response data - if (!currentTask.rxMode) { - currentTask.crcPassFail = 0; - uint16_t response_crc = crc16((uint8_t*)currentTask.commandArray, currentTask.byteCount-1); + if ( hasTasks()){ + Task* currentTask = getTaskById(getNextTaskId()); + if (currentTask == nullptr) return; + if (!currentTask->rxMode) { // Check if a task was found + currentTask->crcPassFail = 0; + uint16_t response_crc = crc16((uint8_t*)currentTask->commandArray, currentTask->byteCount-1); if (_txPin != -1) digitalWrite(_txPin,HIGH); // Send response data with CRC _serial->write(0xFE); _serial->write(0xFE); _serial->write(response_crc >> 8); _serial->write(response_crc & 0xFF); - _serial->write(currentTask.byteCount); - for (int i = 0; i < currentTask.byteCount; i++) { - _serial->write(currentTask.commandArray[i]); + _serial->write(currentTask->byteCount); + for (int i = 0; i < currentTask->byteCount; i++) { + _serial->write(currentTask->commandArray[i]); } _serial->write(0xFD); _serial->write(0xFD); _serial->flush(); if (_txPin != -1) digitalWrite(_txPin,LOW); // delete task command after sending, for now - markTaskCompleted(currentTask.taskID); - } - - if (_serial->available() && currentTask.rxMode) { - + currentTask->rxMode = true; - uint16_t calculated_crc; - int byteCount = 100; - - uint8_t byte_array[byteCount]; - int curByte = _serial->read(); - - if (curByte == 0xFE && flagStart == false) flagStart = true; - else if ( curByte == 0xFE && flagStart == true) { - flagProc = false; - byteCounter = 0; - flagStarted = true; - flagStart = false; - flagEnded = false; - rxStart = true; - rxEnd = false; - crcPass = false; - }else if (flagStarted) { - crc[0] = curByte; - byteCounter++; - flagStarted = false; - } else if (byteCounter == 1) { - crc[1] = curByte; - received_crc = (crc[0] << 8) | crc[1]; - byteCounter++; - } else if (byteCounter == 2) { - byteCount = curByte; - byteCounter++; - } else if (flagEnded == false && byteCounter >= 3) { - received_data[byteCounter-3] = curByte; - byteCounter++; - } - if (curByte == 0xFD && flagEnd == false) flagEnd = true; - else if ( curByte == 0xFD && flagEnd == true) { - flagEnded = true; - flagEnd = false; - rxEnd = true; - byteCount = byteCounter; - byteCounter = 0; - } - if (flagEnded) { - calculated_crc = crc16((uint8_t*)received_data, byteCount-7); - if (received_crc == calculated_crc) { - DIAG(F("Loop CRC PASS")); - crcPass = true; - currentTask.crcPassFail = 1; - }else { - DIAG(F("Loop CRC Fail %x %x"),received_crc,calculated_crc); - currentTask.crcPassFail = -1; - } - flagEnded = false; + } else if (currentTask->rxMode) { + if (_serial->available() && currentTask->rxMode) { - - } - // Check CRC validity - if (crcPass) { - // Data received successfully, process it (e.g., print) - int nodeTo = received_data[0]; - if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. - flagProc = true; - currentTask.gotCallback = true; + uint16_t calculated_crc; + int byteCount = 100; + + uint8_t byte_array[byteCount]; + int curByte = _serial->read(); + + if (curByte == 0xFE && flagStart == false) flagStart = true; + else if ( curByte == 0xFE && flagStart == true) { + flagProc = false; + byteCounter = 0; + flagStarted = true; + flagStart = false; + flagEnded = false; + rxStart = true; + rxEnd = false; + crcPass = false; + memset(received_data, 0, ARRAY_SIZE); + }else if (flagStarted) { + crc[0] = curByte; + byteCounter++; + flagStarted = false; + } else if (byteCounter == 1) { + crc[1] = curByte; + received_crc = (crc[0] << 8) | crc[1]; + byteCounter++; + } else if (byteCounter == 2) { + byteCount = curByte; + byteCounter++; + } else if (flagEnded == false && byteCounter >= 3) { + received_data[byteCounter-3] = curByte; + byteCounter++; } + if (curByte == 0xFD && flagEnd == false) flagEnd = true; + else if ( curByte == 0xFD && flagEnd == true) { + flagEnded = true; + flagEnd = false; + rxEnd = true; + byteCount = byteCounter; + byteCounter = 0; + } + if (flagEnded) { + calculated_crc = crc16((uint8_t*)received_data, byteCount-6); + if (received_crc == calculated_crc) { + //DIAG(F("Loop CRC PASS")); + crcPass = true; + currentTask->crcPassFail = 1; + }else { + //DIAG(F("Loop CRC Fail %x %x"),received_crc,calculated_crc); + currentTask->processed = true; + + currentTask->crcPassFail = -1; + } + flagEnded = false; - } else { - //DIAG(F("IO_RSproto: CRC Error!")); + + } + // Check CRC validity + if (crcPass) { + // Data received successfully, process it (e.g., print) + int nodeTo = received_data[0]; + if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. + flagProc = true; + currentTask->gotCallback = true; + + } + + } else { + //DIAG(F("IO_RSproto: CRC Error!")); + } } - } - // temp debug - - //end debug + // temp debug + - if (flagProc) { - int nodeTo = received_data[0]; - int nodeFr = received_data[1]; - int AddrCode = received_data[2]; - //DIAG(F("From: %i, To: %i | %i %i %i %i %i"), nodeFr,nodeTo, received_data[3], received_data[4], received_data[5], received_data[6],received_data[7]); - //return; - RSprotonode *node = findNode(nodeFr); - switch (AddrCode) { - case EXIOPINS: - {node->setnumDigitalPins(received_data[3]); - node->setnumAnalogPins(received_data[4]); + if (flagProc) { + int nodeTo = received_data[0]; + int nodeFr = received_data[1]; + RSprotonode *node = findNode(nodeFr); + //DIAG(F("Node from %i %i"), nodeFr, node->getNodeID()); + int AddrCode = received_data[2]; + //DIAG(F("From: %i, To: %i | %i %i %i %i %i"), nodeFr,nodeTo, received_data[3], received_data[4], received_data[5], received_data[6],received_data[7]); + //return; + + switch (AddrCode) { + case EXIOPINS: + {node->setnumDigitalPins(received_data[3]); + node->setnumAnalogPins(received_data[4]); - // See if we already have suitable buffers assigned - if (node->getnumDigialPins()>0) { - size_t digitalBytesNeeded = (node->getnumDigialPins() + 7) / 8; - if (node->getdigitalPinBytes() < digitalBytesNeeded) { - // Not enough space, free any existing buffer and allocate a new one - if (node->cleandigitalPinStates(digitalBytesNeeded)) { - node->setdigitalPinBytes(digitalBytesNeeded); - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), nodeFr, digitalBytesNeeded); - //_deviceState = DEVSTATE_FAILED; - node->setdigitalPinBytes(0); - return; + // See if we already have suitable buffers assigned + if (node->getnumDigialPins()>0) { + size_t digitalBytesNeeded = (node->getnumDigialPins() + 7) / 8; + if (node->getdigitalPinBytes() < digitalBytesNeeded) { + // Not enough space, free any existing buffer and allocate a new one + if (node->cleandigitalPinStates(digitalBytesNeeded)) { + node->setdigitalPinBytes(digitalBytesNeeded); + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), nodeFr, digitalBytesNeeded); + //_deviceState = DEVSTATE_FAILED; + node->setdigitalPinBytes(0); + } } } - } - - if (node->getnumAnalogPins()>0) { - size_t analogueBytesNeeded = node->getnumAnalogPins() * 2; - if (node->getanalogPinBytes() < analogueBytesNeeded) { - // Free any existing buffers and allocate new ones. - - if (node->cleanAnalogStates(analogueBytesNeeded)) { - node->setanalogPinBytes(analogueBytesNeeded); - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), nodeFr); - //_deviceState = DEVSTATE_FAILED; - node->setanalogPinBytes(0); - return; + + if (node->getnumAnalogPins()>0) { + size_t analogueBytesNeeded = node->getnumAnalogPins() * 2; + if (node->getanalogPinBytes() < analogueBytesNeeded) { + // Free any existing buffers and allocate new ones. + + if (node->cleanAnalogStates(analogueBytesNeeded)) { + node->setanalogPinBytes(analogueBytesNeeded); + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), nodeFr); + //_deviceState = DEVSTATE_FAILED; + node->setanalogPinBytes(0); + } } } + currentTask->processed = true; + node->resFlag[currentTask->retFlag] = 1; + break;} + case EXIOINITA: { + for (int i = 0; i < node->getnumAnalogPins(); i++) { + node->setanalogPinMap(received_data[i+3], i); + } + currentTask->processed = true; + node->resFlag[currentTask->retFlag] = 1; + break; } - node->resFlag[currentTask.retFlag] = 1; - break;} - case EXIOINITA: { - for (int i = 0; i < node->getnumAnalogPins(); i++) { - node->setanalogPinMap(received_data[i+3], i); + case EXIOVER: { + node->setMajVer(received_data[3]); + node->setMinVer(received_data[4]); + node->setPatVer(received_data[5]); + DIAG(F("EX-IOExpander485: Found node %i v%i.%i.%i"),nodeFr, node->getMajVer(), node->getMinVer(), node->getPatVer()); + //if (!_currentNode->isInitialised()) { + //_currentNode->setInitialised(); + //DIAG(F("EX-IOExpander485: Initialized Node:%d "), nodeFr); + //} + currentTask->processed = true; + node->resFlag[currentTask->retFlag] = 1; + break; } - node->resFlag[currentTask.retFlag] = 1; - break; - } - case EXIOVER: { - node->setMajVer(received_data[3]); - node->setMinVer(received_data[4]); - node->setPatVer(received_data[5]); - DIAG(F("EX-IOExpander485: Found node %i v%i.%i.%i"),node->getNodeID(), node->getMajVer(), node->getMinVer(), node->getPatVer()); - node->resFlag[currentTask.retFlag] = 1; - break; - } - case EXIORDY: { - node->resFlag[currentTask.retFlag] = 1; - break; - } - case EXIOERR: { - node->resFlag[currentTask.retFlag] = -1; - break; - } - case EXIORDD: { - for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { - node->setanalogInputStates(received_data[i+3], i); + case EXIORDY: { + currentTask->processed = true; + node->resFlag[currentTask->retFlag] = 1; + break; } - node->resFlag[currentTask.retFlag] = 1; - break; - } - case EXIORDAN: { - for (int i = 0; i < node->_numAnaloguePins; i++) { - node->setanalogInputBuffer(received_data[i+3], i); + case EXIOERR: { + currentTask->processed = true; + node->resFlag[currentTask->retFlag] = -1; + DIAG(F("Some sort of error was received... WHAT DID YOU DO!")); + break; + } + case EXIORDD: { + for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { + node->setanalogInputStates(received_data[i+3], i); + } + currentTask->processed = true; + node->resFlag[currentTask->retFlag] = 1; + break; + } + case EXIORDAN: { + for (int i = 0; i < node->_numAnaloguePins; i++) { + node->setanalogInputBuffer(received_data[i+3], i); + } + currentTask->processed = true; + node->resFlag[currentTask->retFlag] = 1; + break; } - - node->resFlag[currentTask.retFlag] = 1; - break; } + } - + flagProc = false; + } - if (currentTask.gotCallback) { - - } + if (currentTask->processed) { + markTaskCompleted(currentTask->taskID); + } + } + + + if (_currentMicros - _cycleStartTime >= _cycleTime/* && _currentNode->isInitialised()*/) { + _cycleStartTime = _currentMicros; + if (_currentNode == NULL) _currentNode = _nodeListStart; + RSproto *bus = RSproto::findBus(0); + uint8_t buffB[3]; + buffB[0] = (_currentNode->getNodeID()); + buffB[1] = (0); + buffB[2] = (EXIORDD); + bus->setBusy(); + bus->addTask(buffB, 3, EXIORDD); + buffB[0] = (_currentNode->getNodeID()); + buffB[1] = (0); + buffB[2] = (EXIORDD); + bus->setBusy(); + bus->addTask(buffB, 3, EXIORDAN); + _currentNode = _currentNode->getNext(); + //DIAG(F("Polling")); } } @@ -287,6 +321,7 @@ RSprotonode::RSprotonode(VPIN firstVpin, int nPins, uint8_t nodeID) { _nPins = nPins; _busNo = 0; _nodeID = nodeID; + _initialised = false; memset(resFlag, 0, 255); //bus = bus->findBus(0); //_serial = bus->_serialD; @@ -318,9 +353,8 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun buff[4] = (pullup); unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); - bus->_busy = true; - bus->addTask(bus->taskIDCntr++, buff, 5, EXIODPUP); - bus->_busy = false; + bus->setBusy(); + bus->addTask(buff, 5, EXIODPUP); return true; } @@ -337,11 +371,10 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun buff[5] = lowByte(_firstVpin); unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); - bus->_busy = true; - bus->addTask(bus->taskIDCntr++, buff, 6, EXIOENAN); - bus->_busy = false; + bus->setBusy(); + bus->addTask(buff, 6, EXIOENAN); - return true; + return false; } void RSprotonode::_begin() { @@ -354,28 +387,26 @@ void RSprotonode::_begin() { buff[5] = ((_firstVpin >> 8)); unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); - bus->_busy = true; - bus->addTask(bus->taskIDCntr++, buff, 6, EXIOINIT); - bus->_busy = false; + bus->initTask(); + bus->setBusy(); + bus->addTask(buff, 6, EXIOINIT); buff[0] = (_nodeID); buff[1] = (0); buff[2] = (EXIOINITA); startMillis = millis(); - bus->_busy = true; - bus->addTask(bus->taskIDCntr++, buff, 3, EXIOINITA); - bus->_busy = false; + bus->setBusy(); + bus->addTask(buff, 3, EXIOINITA); buff[0] = (_nodeID); buff[1] = (0); buff[2] = (EXIOVER); startMillis = millis(); - bus->_busy = true; - bus->addTask(bus->taskIDCntr++, buff, 3, EXIOVER); - bus->_busy = false; + bus->setBusy(); + bus->addTask(buff, 3, EXIOVER); - + setInitialised(); #ifdef DIAG_IO _display(); #endif @@ -399,7 +430,8 @@ void RSprotonode::_write(VPIN vpin, int value) { buff[4] = (value); unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); - bus->addTask(bus->taskIDCntr++, buff, 5, EXIOWRD); + bus->setBusy(); + bus->addTask(buff, 5, EXIOWRD); } @@ -430,6 +462,7 @@ void RSprotonode::_write(VPIN vpin, int value) { buff[8] = lowByte(duration); unsigned long startMillis = millis(); RSproto *bus = RSproto::findBus(0); - bus->addTask(bus->taskIDCntr++, buff, 9, EXIOWRAN); + bus->setBusy(); + bus->addTask(buff, 9, EXIOWRAN); } \ No newline at end of file diff --git a/IO_RSproto.h b/IO_RSproto.h index c1404f33..cd64746c 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -93,7 +93,7 @@ class RSprotonode : public IODevice { uint8_t _nodeID; char _type; RSprotonode *_next = NULL; - bool _initialised = false; + bool _initialised; RSproto *bus; HardwareSerial* _serial; enum { @@ -286,7 +286,7 @@ class RSproto : public IODevice { int _operationCount = 0; int _refreshOperation = 0; byte bufferLength; - static const int ARRAY_SIZE = 254; + static const int ARRAY_SIZE = 75; int buffer[ARRAY_SIZE]; byte inCommandPayload; static RSproto *_busList; // linked list of defined bus instances @@ -329,42 +329,82 @@ struct Task { bool gotCallback; bool rxMode; int crcPassFail; - bool completed = false; + bool completed; + bool processed; }; +static const int MAX_TASKS = 100; int taskIDCntr = 0; -Task taskBuffer[100]; // Buffer to hold up to 100 tasks -int taskCount = 0; -void addTask(int id, const uint8_t* cmd, int byteCount, uint8_t retFlag, bool gotCallBack=false, bool rxMode=false, int crcPassFail=0) { - if (taskCount < 100) { // Check if buffer is not full - taskBuffer[taskCount].taskID = id; - memcpy(taskBuffer[taskCount].commandArray, cmd, ARRAY_SIZE); - taskBuffer[taskCount].commandArray[ARRAY_SIZE] = 0; // Ensure null-termination - taskBuffer[taskCount].byteCount = byteCount; - taskBuffer[taskCount].retFlag = retFlag; - taskBuffer[taskCount].gotCallback = false; - taskBuffer[taskCount].rxMode = false; - taskBuffer[taskCount].crcPassFail = 0; - taskBuffer[taskCount].completed = false; - taskCount++; - } else { - Serial.println("Task buffer overflow!"); +Task taskBuffer[MAX_TASKS]; // Buffer to hold up to 100 tasks +int currentTaskIndex = 0; +void initTask() { + for (int i = 0; i 0) { pinMode(_txPin, OUTPUT); digitalWrite(_txPin, LOW); + } #if defined(RSproto_STM_OK) pinMode(RSproto_STM_OK, OUTPUT); From 74cb0c12b009de11e9ad940833324496bc9864ee Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Mon, 23 Dec 2024 08:43:12 -0500 Subject: [PATCH 65/73] saving: works --- IO_RSproto.cpp | 360 +++++++++++++++++++++++-------------------------- IO_RSproto.h | 16 ++- 2 files changed, 183 insertions(+), 193 deletions(-) diff --git a/IO_RSproto.cpp b/IO_RSproto.cpp index f16beb96..9a0bb34d 100644 --- a/IO_RSproto.cpp +++ b/IO_RSproto.cpp @@ -71,14 +71,40 @@ uint16_t RSproto::crc16(uint8_t *data, uint16_t length) { return crc; } +void RSprotonode::_pollDigital(unsigned long currentMicros) { + +} + +void RSprotonode::_pollAnalog(unsigned long currentMicros) { + RSproto *bus = RSproto::findBus(0); + +} + void RSproto::_loop(unsigned long currentMicros) { _currentMicros = currentMicros; //if (_busy == true) return; - + if (_currentNode == NULL) _currentNode = _nodeListStart; + if (!hasTasks() && _currentNode->isInitialised()) { + _cycleStartTime = _currentMicros; + uint8_t buffA[3]; + buffA[0] = (_currentNode->getNodeID()); + buffA[1] = (0); + buffA[2] = (EXIORDD); + addTask(buffA, 3, EXIORDD); + uint8_t buffB[3]; + buffB[0] = (_currentNode->getNodeID()); + buffB[1] = (0); + buffB[2] = (EXIORDAN); + addTask(buffB, 3, EXIORDAN); + //DIAG(F("Polling node: %i"), _currentNode->getNodeID()); + _currentNode = _currentNode->getNext(); + } - if ( hasTasks()){ + //if (currentTask == nullptr) return; + + if ( hasTasks() && _currentMicros - _cycleStartTimeA >= _cycleTime){ + _cycleStartTimeA = _currentMicros; Task* currentTask = getTaskById(getNextTaskId()); - if (currentTask == nullptr) return; if (!currentTask->rxMode) { // Check if a task was found currentTask->crcPassFail = 0; uint16_t response_crc = crc16((uint8_t*)currentTask->commandArray, currentTask->byteCount-1); @@ -99,209 +125,167 @@ void RSproto::_loop(unsigned long currentMicros) { // delete task command after sending, for now currentTask->rxMode = true; - } else if (currentTask->rxMode) { - if (_serial->available() && currentTask->rxMode) { - - - uint16_t calculated_crc; - int byteCount = 100; - - uint8_t byte_array[byteCount]; - int curByte = _serial->read(); - - if (curByte == 0xFE && flagStart == false) flagStart = true; - else if ( curByte == 0xFE && flagStart == true) { - flagProc = false; - byteCounter = 0; - flagStarted = true; - flagStart = false; - flagEnded = false; - rxStart = true; - rxEnd = false; - crcPass = false; - memset(received_data, 0, ARRAY_SIZE); - }else if (flagStarted) { - crc[0] = curByte; - byteCounter++; - flagStarted = false; - } else if (byteCounter == 1) { - crc[1] = curByte; - received_crc = (crc[0] << 8) | crc[1]; - byteCounter++; - } else if (byteCounter == 2) { - byteCount = curByte; - byteCounter++; - } else if (flagEnded == false && byteCounter >= 3) { - received_data[byteCounter-3] = curByte; - byteCounter++; - } - if (curByte == 0xFD && flagEnd == false) flagEnd = true; - else if ( curByte == 0xFD && flagEnd == true) { - flagEnded = true; - flagEnd = false; - rxEnd = true; - byteCount = byteCounter; - byteCounter = 0; - } - if (flagEnded) { - calculated_crc = crc16((uint8_t*)received_data, byteCount-6); - if (received_crc == calculated_crc) { - //DIAG(F("Loop CRC PASS")); - crcPass = true; - currentTask->crcPassFail = 1; - }else { - //DIAG(F("Loop CRC Fail %x %x"),received_crc,calculated_crc); - currentTask->processed = true; - - currentTask->crcPassFail = -1; - } - flagEnded = false; - + //DIAG(F("Polling Task: %i"), currentTask->taskID); + markTaskCompleted(currentTask->taskID); + } + } + if ( _serial->available()) { + + + uint16_t calculated_crc; + int byteCount = 100; + + int curByte = _serial->read(); + + if (curByte == 0xFE && flagStart == false) flagStart = true; + else if ( curByte == 0xFE && flagStart == true) { + flagProc = false; + byteCounter = 0; + flagStarted = true; + flagStart = false; + flagEnded = false; + rxStart = true; + rxEnd = false; + crcPass = false; + memset(received_data, 0, ARRAY_SIZE); + }else if (flagStarted) { + crc[0] = curByte; + byteCounter++; + flagStarted = false; + } else if (byteCounter == 1) { + crc[1] = curByte; + received_crc = (crc[0] << 8) | crc[1]; + byteCounter++; + } else if (byteCounter == 2) { + byteCount = curByte; + byteCounter++; + } else if (flagEnded == false && byteCounter >= 3) { + received_data[byteCounter-3] = curByte; + byteCounter++; + } + if (curByte == 0xFD && flagEnd == false) flagEnd = true; + else if ( curByte == 0xFD && flagEnd == true) { + flagEnded = true; + flagEnd = false; + rxEnd = true; + byteCount = byteCounter; + byteCounter = 0; + } + if (flagEnded) { + calculated_crc = crc16((uint8_t*)received_data, byteCount-6); + if (received_crc == calculated_crc) { + //DIAG(F("Loop CRC PASS")); + crcPass = true; + }else { + //DIAG(F("Loop CRC Fail %x %x"),received_crc,calculated_crc); + } + flagEnded = false; + - } - // Check CRC validity - if (crcPass) { - // Data received successfully, process it (e.g., print) - int nodeTo = received_data[0]; - if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. - flagProc = true; - currentTask->gotCallback = true; - - } - - } else { - //DIAG(F("IO_RSproto: CRC Error!")); - } + } + // Check CRC validity + if (crcPass) { + // Data received successfully, process it (e.g., print) + int nodeTo = received_data[0]; + if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. + flagProc = true; } - - // temp debug + } else { + //DIAG(F("IO_RSproto: CRC Error!")); + } - if (flagProc) { - int nodeTo = received_data[0]; - int nodeFr = received_data[1]; - RSprotonode *node = findNode(nodeFr); - //DIAG(F("Node from %i %i"), nodeFr, node->getNodeID()); - int AddrCode = received_data[2]; - //DIAG(F("From: %i, To: %i | %i %i %i %i %i"), nodeFr,nodeTo, received_data[3], received_data[4], received_data[5], received_data[6],received_data[7]); - //return; - - switch (AddrCode) { - case EXIOPINS: - {node->setnumDigitalPins(received_data[3]); - node->setnumAnalogPins(received_data[4]); + if (flagProc) { + int nodeTo = received_data[0]; + int nodeFr = received_data[1]; + RSprotonode *node = findNode(nodeFr); + //DIAG(F("Node from %i %i"), nodeFr, node->getNodeID()); + int AddrCode = received_data[2]; + //DIAG(F("From: %i, To: %i | %i %i %i %i %i"), nodeFr,nodeTo, received_data[3], received_data[4], received_data[5], received_data[6],received_data[7]); + //return; + + switch (AddrCode) { + case EXIOPINS: + {node->setnumDigitalPins(received_data[3]); + node->setnumAnalogPins(received_data[4]); - // See if we already have suitable buffers assigned - if (node->getnumDigialPins()>0) { - size_t digitalBytesNeeded = (node->getnumDigialPins() + 7) / 8; - if (node->getdigitalPinBytes() < digitalBytesNeeded) { - // Not enough space, free any existing buffer and allocate a new one - if (node->cleandigitalPinStates(digitalBytesNeeded)) { - node->setdigitalPinBytes(digitalBytesNeeded); - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), nodeFr, digitalBytesNeeded); - //_deviceState = DEVSTATE_FAILED; - node->setdigitalPinBytes(0); - } + // See if we already have suitable buffers assigned + if (node->getnumDigialPins()>0) { + size_t digitalBytesNeeded = (node->getnumDigialPins() + 7) / 8; + if (node->getdigitalPinBytes() < digitalBytesNeeded) { + // Not enough space, free any existing buffer and allocate a new one + if (node->cleandigitalPinStates(digitalBytesNeeded)) { + node->setdigitalPinBytes(digitalBytesNeeded); + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), nodeFr, digitalBytesNeeded); + //_deviceState = DEVSTATE_FAILED; + node->setdigitalPinBytes(0); } } - - if (node->getnumAnalogPins()>0) { - size_t analogueBytesNeeded = node->getnumAnalogPins() * 2; - if (node->getanalogPinBytes() < analogueBytesNeeded) { - // Free any existing buffers and allocate new ones. - - if (node->cleanAnalogStates(analogueBytesNeeded)) { - node->setanalogPinBytes(analogueBytesNeeded); - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), nodeFr); - //_deviceState = DEVSTATE_FAILED; - node->setanalogPinBytes(0); - } + } + + if (node->getnumAnalogPins()>0) { + size_t analogueBytesNeeded = node->getnumAnalogPins() * 2; + if (node->getanalogPinBytes() < analogueBytesNeeded) { + // Free any existing buffers and allocate new ones. + + if (node->cleanAnalogStates(analogueBytesNeeded)) { + node->setanalogPinBytes(analogueBytesNeeded); + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), nodeFr); + //_deviceState = DEVSTATE_FAILED; + node->setanalogPinBytes(0); } } - currentTask->processed = true; - node->resFlag[currentTask->retFlag] = 1; - break;} - case EXIOINITA: { - for (int i = 0; i < node->getnumAnalogPins(); i++) { - node->setanalogPinMap(received_data[i+3], i); - } - currentTask->processed = true; - node->resFlag[currentTask->retFlag] = 1; - break; - } - case EXIOVER: { - node->setMajVer(received_data[3]); - node->setMinVer(received_data[4]); - node->setPatVer(received_data[5]); - DIAG(F("EX-IOExpander485: Found node %i v%i.%i.%i"),nodeFr, node->getMajVer(), node->getMinVer(), node->getPatVer()); - //if (!_currentNode->isInitialised()) { - //_currentNode->setInitialised(); - //DIAG(F("EX-IOExpander485: Initialized Node:%d "), nodeFr); - //} - currentTask->processed = true; - node->resFlag[currentTask->retFlag] = 1; - break; } - case EXIORDY: { - currentTask->processed = true; - node->resFlag[currentTask->retFlag] = 1; - break; + break;} + case EXIOINITA: { + for (int i = 0; i < node->getnumAnalogPins(); i++) { + node->setanalogPinMap(received_data[i+3], i); } - case EXIOERR: { - currentTask->processed = true; - node->resFlag[currentTask->retFlag] = -1; - DIAG(F("Some sort of error was received... WHAT DID YOU DO!")); - break; - } - case EXIORDD: { - for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { - node->setanalogInputStates(received_data[i+3], i); - } - currentTask->processed = true; - node->resFlag[currentTask->retFlag] = 1; - break; - } - case EXIORDAN: { - for (int i = 0; i < node->_numAnaloguePins; i++) { - node->setanalogInputBuffer(received_data[i+3], i); - } - currentTask->processed = true; - node->resFlag[currentTask->retFlag] = 1; - break; + break; + } + case EXIOVER: { + node->setMajVer(received_data[3]); + node->setMinVer(received_data[4]); + node->setPatVer(received_data[5]); + DIAG(F("EX-IOExpander485: Found node %i v%i.%i.%i"),node->getNodeID(), node->getMajVer(), node->getMinVer(), node->getPatVer()); + node->setInitialised(); + //DIAG(F("EX-IOExpander485: Initialized Node:%d "), node->getNodeID()); + break; + } + case EXIORDY: { + break; + } + case EXIOERR: { + DIAG(F("EX-IOExplorer485: Some sort of error was received... WHAT DID YOU DO!")); // ;-) + break; + } + case EXIORDAN: { + for (int i = 0; i < node->_numAnaloguePins; i++) { + node->setanalogInputBuffer(received_data[i+3], i); } + + break; } - + case EXIORDD: { + for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { + node->setdigitalInputStates(received_data[i+3], i); + } + break; + } } - flagProc = false; } - if (currentTask->processed) { - markTaskCompleted(currentTask->taskID); - } + flagProc = false; } - - if (_currentMicros - _cycleStartTime >= _cycleTime/* && _currentNode->isInitialised()*/) { - _cycleStartTime = _currentMicros; - if (_currentNode == NULL) _currentNode = _nodeListStart; - RSproto *bus = RSproto::findBus(0); - uint8_t buffB[3]; - buffB[0] = (_currentNode->getNodeID()); - buffB[1] = (0); - buffB[2] = (EXIORDD); - bus->setBusy(); - bus->addTask(buffB, 3, EXIORDD); + // temp debug + - buffB[0] = (_currentNode->getNodeID()); - buffB[1] = (0); - buffB[2] = (EXIORDD); - bus->setBusy(); - bus->addTask(buffB, 3, EXIORDAN); - _currentNode = _currentNode->getNext(); - //DIAG(F("Polling")); - } + + + } // Link to chain of RSproto instances, left over from RSproto template. @@ -406,7 +390,7 @@ void RSprotonode::_begin() { bus->addTask(buff, 3, EXIOVER); - setInitialised(); + //setInitialised(); #ifdef DIAG_IO _display(); #endif diff --git a/IO_RSproto.h b/IO_RSproto.h index cd64746c..fbd8d8ca 100644 --- a/IO_RSproto.h +++ b/IO_RSproto.h @@ -122,7 +122,9 @@ class RSprotonode : public IODevice { Bounce = 4, // For semaphores/turnouts with a bit of bounce!! NoPowerOff = 0x80, // Flag to be ORed in to suppress power off after move. }; - + void _pollDigital(unsigned long currentMicros); + void _pollAnalog(unsigned long currentMicros); + uint8_t _numDigitalPins = 0; uint8_t getnumDigialPins() { return _numDigitalPins; @@ -277,6 +279,7 @@ class RSproto : public IODevice { // Here we define the device-specific variables. uint8_t _busNo; unsigned long _cycleStartTime = 0; + unsigned long _cycleStartTimeA = 0; unsigned long _timeoutStart = 0; unsigned long _cycleTime; // target time between successive read/write cycles, microseconds unsigned long _timeoutPeriod; // timeout on read responses, in microseconds. @@ -286,7 +289,7 @@ class RSproto : public IODevice { int _operationCount = 0; int _refreshOperation = 0; byte bufferLength; - static const int ARRAY_SIZE = 75; + static const int ARRAY_SIZE = 150; int buffer[ARRAY_SIZE]; byte inCommandPayload; static RSproto *_busList; // linked list of defined bus instances @@ -321,7 +324,7 @@ class RSproto : public IODevice { int byteCounter = 0; public: struct Task { - static const int ARRAY_SIZE = 254; + static const int ARRAY_SIZE = 150; int taskID; uint8_t commandArray[ARRAY_SIZE]; int byteCount; @@ -332,7 +335,7 @@ struct Task { bool completed; bool processed; }; -static const int MAX_TASKS = 100; +static const int MAX_TASKS = 50; int taskIDCntr = 0; Task taskBuffer[MAX_TASKS]; // Buffer to hold up to 100 tasks int currentTaskIndex = 0; @@ -363,7 +366,7 @@ void addTask(const uint8_t* cmd, int byteCount, uint8_t retFlag) { DIAG(F("Task Buffer Full!")); return; } - for (int i = 0; i < ARRAY_SIZE; i++) taskBuffer[emptySlot].commandArray[i] = cmd[i]; + for (int i = 0; i < byteCount; i++) taskBuffer[emptySlot].commandArray[i] = cmd[i]; taskBuffer[emptySlot].byteCount = byteCount; taskBuffer[emptySlot].retFlag = retFlag; taskBuffer[emptySlot].rxMode = false; @@ -447,6 +450,9 @@ uint16_t crc16(uint8_t *data, uint16_t length); uint16_t _pullup; uint16_t _pin; int8_t _txPin; + int8_t getTxPin() { + return _txPin; + } bool _busy = false; void setBusy() { _busy = true; From 8a28cf1d213a6fb7895eb423412d8659948c4169 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Thu, 26 Dec 2024 03:16:32 -0500 Subject: [PATCH 66/73] rename driver class --- IO_RSproto.cpp => IO_EXIO485.cpp | 112 ++++++---------- IO_RSproto.h => IO_EXIO485.h | 221 ++++++++++++++++++++++--------- 2 files changed, 193 insertions(+), 140 deletions(-) rename IO_RSproto.cpp => IO_EXIO485.cpp (78%) rename IO_RSproto.h => IO_EXIO485.h (74%) diff --git a/IO_RSproto.cpp b/IO_EXIO485.cpp similarity index 78% rename from IO_RSproto.cpp rename to IO_EXIO485.cpp index 9a0bb34d..c44d6299 100644 --- a/IO_RSproto.cpp +++ b/IO_EXIO485.cpp @@ -18,7 +18,7 @@ * along with CommandStation. If not, see . */ -#include "IO_RSproto.h" +#include "IO_EXIO485.h" #include "defines.h" static const byte PAYLOAD_FALSE = 0; @@ -27,11 +27,11 @@ static const byte PAYLOAD_STRING = 2; /************************************************************ - * RSproto implementation + * EXIO485 implementation ************************************************************/ -// Constructor for RSproto -RSproto::RSproto(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin, int cycleTime) { +// Constructor for EXIO485 +EXIO485::EXIO485(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin, int cycleTime) { _serial = &serial; _baud = baud; @@ -43,20 +43,20 @@ RSproto::RSproto(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8 // Add device to HAL device chain IODevice::addDevice(this); - // Add bus to RSproto chain. + // Add bus to EXIO485 chain. _nextBus = _busList; _busList = this; } /* -= _loop =- // -// Main loop function for RSproto. +// Main loop function for EXIO485. // Work through list of nodes. For each node, in separate loop entries // When the slot time has finished, move on to the next device. */ // CRC-16 implementation (replace with your preferred CRC library if needed) -uint16_t RSproto::crc16(uint8_t *data, uint16_t length) { +uint16_t EXIO485::crc16(uint8_t *data, uint16_t length) { uint16_t crc = 0xFFFF; for (uint16_t i = 0; i < length; i++) { crc ^= data[i]; @@ -71,18 +71,8 @@ uint16_t RSproto::crc16(uint8_t *data, uint16_t length) { return crc; } -void RSprotonode::_pollDigital(unsigned long currentMicros) { - -} - -void RSprotonode::_pollAnalog(unsigned long currentMicros) { - RSproto *bus = RSproto::findBus(0); - -} - -void RSproto::_loop(unsigned long currentMicros) { +void EXIO485::_loop(unsigned long currentMicros) { _currentMicros = currentMicros; - //if (_busy == true) return; if (_currentNode == NULL) _currentNode = _nodeListStart; if (!hasTasks() && _currentNode->isInitialised()) { _cycleStartTime = _currentMicros; @@ -96,12 +86,9 @@ void RSproto::_loop(unsigned long currentMicros) { buffB[1] = (0); buffB[2] = (EXIORDAN); addTask(buffB, 3, EXIORDAN); - //DIAG(F("Polling node: %i"), _currentNode->getNodeID()); _currentNode = _currentNode->getNext(); } - //if (currentTask == nullptr) return; - if ( hasTasks() && _currentMicros - _cycleStartTimeA >= _cycleTime){ _cycleStartTimeA = _currentMicros; Task* currentTask = getTaskById(getNextTaskId()); @@ -125,7 +112,6 @@ void RSproto::_loop(unsigned long currentMicros) { // delete task command after sending, for now currentTask->rxMode = true; - //DIAG(F("Polling Task: %i"), currentTask->taskID); markTaskCompleted(currentTask->taskID); } } @@ -174,11 +160,8 @@ void RSproto::_loop(unsigned long currentMicros) { if (flagEnded) { calculated_crc = crc16((uint8_t*)received_data, byteCount-6); if (received_crc == calculated_crc) { - //DIAG(F("Loop CRC PASS")); crcPass = true; - }else { - //DIAG(F("Loop CRC Fail %x %x"),received_crc,calculated_crc); - } + } flagEnded = false; @@ -191,18 +174,12 @@ void RSproto::_loop(unsigned long currentMicros) { flagProc = true; } - } else { - //DIAG(F("IO_RSproto: CRC Error!")); } if (flagProc) { - int nodeTo = received_data[0]; int nodeFr = received_data[1]; - RSprotonode *node = findNode(nodeFr); - //DIAG(F("Node from %i %i"), nodeFr, node->getNodeID()); + EXIO485node *node = findNode(nodeFr); int AddrCode = received_data[2]; - //DIAG(F("From: %i, To: %i | %i %i %i %i %i"), nodeFr,nodeTo, received_data[3], received_data[4], received_data[5], received_data[6],received_data[7]); - //return; switch (AddrCode) { case EXIOPINS: @@ -218,7 +195,6 @@ void RSproto::_loop(unsigned long currentMicros) { node->setdigitalPinBytes(digitalBytesNeeded); } else { DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), nodeFr, digitalBytesNeeded); - //_deviceState = DEVSTATE_FAILED; node->setdigitalPinBytes(0); } } @@ -233,7 +209,6 @@ void RSproto::_loop(unsigned long currentMicros) { node->setanalogPinBytes(analogueBytesNeeded); } else { DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), nodeFr); - //_deviceState = DEVSTATE_FAILED; node->setanalogPinBytes(0); } } @@ -251,14 +226,13 @@ void RSproto::_loop(unsigned long currentMicros) { node->setPatVer(received_data[5]); DIAG(F("EX-IOExpander485: Found node %i v%i.%i.%i"),node->getNodeID(), node->getMajVer(), node->getMinVer(), node->getPatVer()); node->setInitialised(); - //DIAG(F("EX-IOExpander485: Initialized Node:%d "), node->getNodeID()); break; } case EXIORDY: { break; } case EXIOERR: { - DIAG(F("EX-IOExplorer485: Some sort of error was received... WHAT DID YOU DO!")); // ;-) + DIAG(F("EX-IOExplorer485: Some sort of error was received...")); // ;-) break; } case EXIORDAN: { @@ -288,35 +262,33 @@ void RSproto::_loop(unsigned long currentMicros) { } -// Link to chain of RSproto instances, left over from RSproto template. -RSproto *RSproto::_busList = NULL; +// Link to chain of EXIO485 instances, left over from EXIO485 template. +EXIO485 *EXIO485::_busList = NULL; /************************************************************ - * RSprotonode implementation + * EXIO485node implementation ************************************************************/ -/* -= RSprotonode =- +/* -= EXIO485node =- // -// Constructor for RSprotonode object +// Constructor for EXIO485node object */ -RSprotonode::RSprotonode(VPIN firstVpin, int nPins, uint8_t nodeID) { +EXIO485node::EXIO485node(VPIN firstVpin, int nPins, uint8_t nodeID) { _firstVpin = firstVpin; _nPins = nPins; _busNo = 0; _nodeID = nodeID; _initialised = false; memset(resFlag, 0, 255); - //bus = bus->findBus(0); - //_serial = bus->_serialD; if (_nodeID > 252) _nodeID = 252; // cannot have a node with the frame flags if (_nodeID < 1) _nodeID = 1; // cannot have a node with the master ID // Add this device to HAL device list IODevice::addDevice(this); _display(); - // Add RSprotonode to RSproto object. - RSproto *bus = RSproto::findBus(_busNo); + // Add EXIO485node to EXIO485 object. + EXIO485 *bus = EXIO485::findBus(_busNo); if (bus != NULL) { bus->addNode(this); return; @@ -324,7 +296,7 @@ RSprotonode::RSprotonode(VPIN firstVpin, int nPins, uint8_t nodeID) { } -bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) { +bool EXIO485node::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCount, int params[]) { if (paramCount != 1) return false; int pin = vpin - _firstVpin; @@ -335,33 +307,30 @@ bool RSprotonode::_configure(VPIN vpin, ConfigTypeEnum configType, int paramCoun buff[2] = (EXIODPUP); buff[3] = (pin); buff[4] = (pullup); - unsigned long startMillis = millis(); - RSproto *bus = RSproto::findBus(0); + EXIO485 *bus = EXIO485::findBus(0); bus->setBusy(); bus->addTask(buff, 5, EXIODPUP); return true; } - int RSprotonode::_configureAnalogIn(VPIN vpin) { + int EXIO485node::_configureAnalogIn(VPIN vpin) { int pin = vpin - _firstVpin; - //RSproto *mainrs = RSproto::findBus(_busNo); uint8_t buff[ARRAY_SIZE]; buff[0] = (_nodeID); buff[1] = (0); buff[2] = (EXIOENAN); buff[3] = (pin); - buff[4] = highByte(_firstVpin); - buff[5] = lowByte(_firstVpin); - unsigned long startMillis = millis(); - RSproto *bus = RSproto::findBus(0); + buff[4] = lowByte(_firstVpin); + buff[5] = highByte(_firstVpin); + EXIO485 *bus = EXIO485::findBus(0); bus->setBusy(); bus->addTask(buff, 6, EXIOENAN); return false; } -void RSprotonode::_begin() { +void EXIO485node::_begin() { uint8_t buff[ARRAY_SIZE]; buff[0] = (_nodeID); buff[1] = (0); @@ -369,8 +338,7 @@ void RSprotonode::_begin() { buff[3] = (_nPins); buff[4] = ((_firstVpin & 0xFF)); buff[5] = ((_firstVpin >> 8)); - unsigned long startMillis = millis(); - RSproto *bus = RSproto::findBus(0); + EXIO485 *bus = EXIO485::findBus(0); bus->initTask(); bus->setBusy(); bus->addTask(buff, 6, EXIOINIT); @@ -378,32 +346,28 @@ void RSprotonode::_begin() { buff[0] = (_nodeID); buff[1] = (0); buff[2] = (EXIOINITA); - startMillis = millis(); bus->setBusy(); bus->addTask(buff, 3, EXIOINITA); buff[0] = (_nodeID); buff[1] = (0); buff[2] = (EXIOVER); - startMillis = millis(); bus->setBusy(); bus->addTask(buff, 3, EXIOVER); - - //setInitialised(); #ifdef DIAG_IO _display(); #endif } -int RSprotonode::_read(VPIN vpin) { +int EXIO485node::_read(VPIN vpin) { if (_deviceState == DEVSTATE_FAILED) return 0; int pin = vpin - _firstVpin; uint8_t pinByte = pin / 8; bool value = bitRead(_digitalInputStates[pinByte], pin - pinByte * 8); return value; } -void RSprotonode::_write(VPIN vpin, int value) { +void EXIO485node::_write(VPIN vpin, int value) { if (_deviceState == DEVSTATE_FAILED) return; int pin = vpin - _firstVpin; uint8_t buff[ARRAY_SIZE]; @@ -412,14 +376,13 @@ void RSprotonode::_write(VPIN vpin, int value) { buff[2] = (EXIOWRD); buff[3] = (pin); buff[4] = (value); - unsigned long startMillis = millis(); - RSproto *bus = RSproto::findBus(0); + EXIO485 *bus = EXIO485::findBus(0); bus->setBusy(); bus->addTask(buff, 5, EXIOWRD); } - int RSprotonode::_readAnalogue(VPIN vpin) { + int EXIO485node::_readAnalogue(VPIN vpin) { if (_deviceState == DEVSTATE_FAILED) return 0; int pin = vpin - _firstVpin; for (uint8_t aPin = 0; aPin < _numAnaloguePins; aPin++) { @@ -432,20 +395,19 @@ void RSprotonode::_write(VPIN vpin, int value) { return -1; // pin not found in table } - void RSprotonode::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) { + void EXIO485node::_writeAnalogue(VPIN vpin, int value, uint8_t profile, uint16_t duration) { int pin = vpin - _firstVpin; uint8_t buff[ARRAY_SIZE]; buff[0] = (_nodeID); buff[1] = (0); buff[2] = (EXIOWRAN); buff[3] = (pin); - buff[4] = highByte(value); - buff[5] = lowByte(value); + buff[4] = lowByte(value); + buff[5] = highByte(value); buff[6] = (profile); - buff[7] = highByte(duration); - buff[8] = lowByte(duration); - unsigned long startMillis = millis(); - RSproto *bus = RSproto::findBus(0); + buff[7] = lowByte(duration); + buff[8] = highByte(duration); + EXIO485 *bus = EXIO485::findBus(0); bus->setBusy(); bus->addTask(buff, 9, EXIOWRAN); diff --git a/IO_RSproto.h b/IO_EXIO485.h similarity index 74% rename from IO_RSproto.h rename to IO_EXIO485.h index fbd8d8ca..7974fc5f 100644 --- a/IO_RSproto.h +++ b/IO_EXIO485.h @@ -19,36 +19,36 @@ */ /* - * RSproto + * EXIO485 * ======= - * To define a RSproto, example syntax: - * RSproto::create(busNo, serial, baud[, pin]); + * To define a EXIO485, example syntax: + * EXIO485::create(busNo, serial, baud[, pin]); * * busNo = the Bus no of the instance. should = 0, unless more than one bus configured for some reason. * 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 RSproto module's DE and !RE terminals for half-duplex operation (default -1) + * pin = pin number connected to EXIO485 module's DE and !RE terminals for half-duplex operation (default -1) * if omitted (default), hardware MUST support full-duplex opperation! * * - * RSprotoNode + * EXIO485Node * ======== - * To define a RSproto node and associate it with a RSproto bus, - * RSprotonode::create(firstVPIN, numVPINs, nodeID); + * To define a EXIO485 node and associate it with a EXIO485 bus, + * EXIO485node::create(firstVPIN, numVPINs, nodeID); * * firstVPIN = first vpin in block allocated to this device * numVPINs = number of vpins * nodeID = 1-251 */ -#ifndef IO_RS485_H -#define IO_RS485_H +#ifndef IO_EXIO485_H +#define IO_EXIO485_H #include "IODevice.h" -class RSproto; -class RSprotonode; +class EXIO485; +class EXIO485node; @@ -57,44 +57,137 @@ class RSprotonode; #endif /********************************************************************** - * taskBuffer class + * Data Structure * - * this stores the task list, and processes the data within it for - * sending. it also handles the incomming data responce. * Data Frame: - * 0xFD : toNode : fromNode : ~data packet~ : 0xFE - * Start: TO : FROM : DATA : End + * 0xFE : 0xFE : CRC : CRC : ByteCount : DataPacket : 0xFD : 0xFD + * -------------------------------------------------------------- + * Start Frame : CRC Bytes : Data Size : Data : End Frame * - * Data frame must always start with the Start byte, follow with the - * destination (toNode), follow with the Source (fromNode), contain - * the data packet, and follow with the End byte. + * Data frame must always start with the Start Frame bytes (two Bytes), + * follow with the CRC bytes (two bytes), the data byte count + * (one byte), the Data Packet (variable bytes), and the end Frame + * Bytes. * * * Data Packet: - * Command Byte : ~Command Params~ + * NodeTo : NodeFrom : AddrCode : ~Command Params~ + * ----------------------------------------------- + * NodeTo = where the packet is destined for. + * NodeFrom = where the packet came from. + * Address Code = from EXIO enumeration. + * Command Params: * - * Data Packet must always precede the parameters with the Command byte. - * this way the data is processed by the correct routine. + * EXIOINIT:TX CS + * -------- + * nPins : FirstPinL : FirstPinH + * ----------------------------- + * nPins = Number of allocated pins. + * FirstPinL = First VPIN lowByte. + * FirstPinH = First VPIN highByte. + * + * Sends the allocated pins. + * + * EXIOINITA: Tx CS + * -=no parameters, just a header=- + * + * requests the analog pin map from the node. + * + * EXIOVER: Tx CS + * -=no parameters=- + * + * requests the node software version, but as yet to do anything with it + * + * EXIODPUP: Tx CS + * pin : pullup + * + * pin = VPIN number + * pullup = 1 - Pullup, 0 - no pullup + * configures a digital pin for input + * + * EXIOENAN: TX CS + * pin : FirstPinL : FirstPinH + * + * pin = VPIN number + * FirstPinL = first pin lowByte + * FirstPinH = first pin highByte + * + * EXIOWRD: TX CS + * pin : value + * + * pin = VPIN number + * value = 1 or 0 + * + * EXIOWRAN: TX CS + * pin : valueL : valueH : profile : durationL : durationH + * + * pin = VPIN Number + * valueL = value lowByte + * valueH = value highByte + * profile = servo profile + * dueationL = duration lowByte + * durationH = duration highByte + * + * EXIORDD: TX CS + * -=No Parameters=- + * + * Requests digital pin states. + * + * EXIORDAN: TX CS + * -=no parameters=- + * + * Requests analog pin states. + * + * EXIOPINS: TX Node (EXIOINIT) + * numDigital : numAnalog + * + * numDigital = number of digital capable pins + * numAnalog = number of analog capable pins + * + * EXIOINITA: TX Node (EXIOINITA) + * ~analog pin map~ + * + * each byte is a analog pin map value, variable length. + * + * EXIORDY/EXIOERR: TX Node (EXIODPUP, EXIOWRD, EXIOENAN, EXIOWRAN) + * -=no parameters=- + * + * Responds EXIORDY for OK, and EXIOERR for FAIL. + * + * EXIORDAN: TX Node (EXIORDAN) + * ~analog pin states~ + * + * each byte is a pin state value, perhaps in lowByte/higeByte config. + * + * EXIORDD: TX Node (EXIORDD) + * ~digital pin states~ + * + * each byte is a 8-bit grouping of pinstates. + * + * EXIOVER: TX Node (EXIOVER) + * Major Version : Minor Version : Patch Version + * + * each byte represents a numeric version value. **********************************************************************/ /********************************************************************** - * RSprotonode class + * EXIO485node class * - * This encapsulates the state associated with a single RSproto node, + * This encapsulates the state associated with a single EXIO485 node, * which includes the nodeID, number of discrete inputs and coils, and * the states of the discrete inputs and coils. **********************************************************************/ -class RSprotonode : public IODevice { +class EXIO485node : public IODevice { private: uint8_t _busNo; uint8_t _nodeID; char _type; - RSprotonode *_next = NULL; + EXIO485node *_next = NULL; bool _initialised; - RSproto *bus; + EXIO485 *bus; HardwareSerial* _serial; enum { EXIOINIT = 0xE0, // Flag to initialise setup procedure @@ -112,7 +205,7 @@ class RSprotonode : public IODevice { }; static const int ARRAY_SIZE = 254; public: - static RSprotonode *_nodeList; + static EXIO485node *_nodeList; enum ProfileType : int { Instant = 0, // Moves immediately between positions (if duration not specified) UseDuration = 0, // Use specified duration @@ -122,9 +215,7 @@ class RSprotonode : public IODevice { Bounce = 4, // For semaphores/turnouts with a bit of bounce!! NoPowerOff = 0x80, // Flag to be ORed in to suppress power off after move. }; - void _pollDigital(unsigned long currentMicros); - void _pollAnalog(unsigned long currentMicros); - + uint8_t _numDigitalPins = 0; uint8_t getnumDigialPins() { return _numDigitalPins; @@ -226,19 +317,19 @@ class RSprotonode : public IODevice { int resFlag[255]; bool _initalized; static void create(VPIN firstVpin, int nPins, uint8_t nodeID) { - if (checkNoOverlap(firstVpin, nPins)) new RSprotonode(firstVpin, nPins, nodeID); + if (checkNoOverlap(firstVpin, nPins)) new EXIO485node(firstVpin, nPins, nodeID); } - RSprotonode(VPIN firstVpin, int nPins, uint8_t nodeID); + EXIO485node(VPIN firstVpin, int nPins, uint8_t nodeID); uint8_t getNodeID() { return _nodeID; } - RSprotonode *getNext() { + EXIO485node *getNext() { return _next; } - void setNext(RSprotonode *node) { + void setNext(EXIO485node *node) { _next = node; } bool isInitialised() { @@ -267,14 +358,14 @@ class RSprotonode : public IODevice { }; /********************************************************************** - * RSproto class + * EXIO485 class * * This encapsulates the properties state of the bus and the - * transmission and reception of data across that bus. Each RSproto - * object owns a set of RSprotonode objects which represent the nodes + * transmission and reception of data across that bus. Each EXIO485 + * object owns a set of EXIO485node objects which represent the nodes * attached to that bus. **********************************************************************/ -class RSproto : public IODevice { +class EXIO485 : public IODevice { private: // Here we define the device-specific variables. uint8_t _busNo; @@ -292,7 +383,7 @@ class RSproto : public IODevice { static const int ARRAY_SIZE = 150; int buffer[ARRAY_SIZE]; byte inCommandPayload; - static RSproto *_busList; // linked list of defined bus instances + static EXIO485 *_busList; // linked list of defined bus instances bool waitReceive = false; int _waitCounter = 0; int _waitCounterB = 0; @@ -310,10 +401,10 @@ class RSproto : public IODevice { - RSprotonode *_nodeListStart = NULL, *_nodeListEnd = NULL; - RSprotonode *_currentNode = NULL; + EXIO485node *_nodeListStart = NULL, *_nodeListEnd = NULL; + EXIO485node *_currentNode = NULL; uint16_t _receiveDataIndex = 0; // Index of next data byte to be received. - RSproto *_nextBus = NULL; // Pointer to next bus instance in list. + EXIO485 *_nextBus = NULL; // Pointer to next bus instance in list. // Helper function for error handling void reportError(uint8_t status, bool fail=true) { @@ -442,7 +533,7 @@ uint16_t crc16(uint8_t *data, uint16_t length); EXIOERR = 0xEF, // Flag we've received an error }; static void create(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin=-1, int cycleTime=500) { - new RSproto(busNo, serial, baud, txPin, cycleTime); + new EXIO485(busNo, serial, baud, txPin, cycleTime); } HardwareSerial* _serial; int _CommMode = 0; @@ -475,17 +566,17 @@ uint16_t crc16(uint8_t *data, uint16_t length); digitalWrite(_txPin, LOW); } - #if defined(RSproto_STM_OK) - pinMode(RSproto_STM_OK, OUTPUT); - ArduinoPins::fastWriteDigital(RSproto_STM_OK,LOW); + #if defined(EXIO485_STM_OK) + pinMode(EXIO485_STM_OK, OUTPUT); + ArduinoPins::fastWriteDigital(EXIO485_STM_OK,LOW); #endif - #if defined(RSproto_STM_FAIL) - pinMode(RSproto_STM_FAIL, OUTPUT); - ArduinoPins::fastWriteDigital(RSproto_STM_FAIL,LOW); + #if defined(EXIO485_STM_FAIL) + pinMode(EXIO485_STM_FAIL, OUTPUT); + ArduinoPins::fastWriteDigital(EXIO485_STM_FAIL,LOW); #endif - #if defined(RSproto_STM_COMM) - pinMode(RSproto_STM_COMM, OUTPUT); - ArduinoPins::fastWriteDigital(RSproto_STM_COMM,LOW); + #if defined(EXIO485_STM_COMM) + pinMode(EXIO485_STM_COMM, OUTPUT); + ArduinoPins::fastWriteDigital(EXIO485_STM_COMM,LOW); #endif #if defined(DIAG_IO) @@ -503,9 +594,9 @@ uint16_t crc16(uint8_t *data, uint16_t length); _deviceState == DEVSTATE_FAILED ? F("OFFLINE") : F("OK")); } - // Locate RSprotonode object with specified nodeID. - RSprotonode *findNode(uint8_t nodeID) { - for (RSprotonode *node = _nodeListStart; node != NULL; node = node->getNext()) { + // Locate EXIO485node object with specified nodeID. + EXIO485node *findNode(uint8_t nodeID) { + for (EXIO485node *node = _nodeListStart; node != NULL; node = node->getNext()) { if (node->getNodeID() == nodeID) return node; } @@ -514,36 +605,36 @@ uint16_t crc16(uint8_t *data, uint16_t length); bool nodesInitialized() { bool retval = true; - for (RSprotonode *node = _nodeListStart; node != NULL; node = node->getNext()) { + for (EXIO485node *node = _nodeListStart; node != NULL; node = node->getNext()) { if (node->_initalized == false) retval = false; } return retval; } - // Add new RSprotonode to the list of nodes for this bus. - void addNode(RSprotonode *newNode) { + // Add new EXIO485node to the list of nodes for this bus. + void addNode(EXIO485node *newNode) { if (!_nodeListStart) _nodeListStart = newNode; if (!_nodeListEnd) _nodeListEnd = newNode; else _nodeListEnd->setNext(newNode); - //DIAG(F("RSproto: 260h nodeID:%d _nodeListStart:%d _nodeListEnd:%d"), newNode, _nodeListStart, _nodeListEnd); + //DIAG(F("EXIO485: 260h nodeID:%d _nodeListStart:%d _nodeListEnd:%d"), newNode, _nodeListStart, _nodeListEnd); } protected: - RSproto(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin, int cycleTime); + EXIO485(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin, int cycleTime); public: uint8_t getBusNumber() { return _busNo; } - RSproto *getNext() { + EXIO485 *getNext() { return _nextBus; } - static RSproto *findBus(uint8_t busNo) { - for (RSproto *bus = _busList; bus != NULL; bus = bus->getNext()) { + static EXIO485 *findBus(uint8_t busNo) { + for (EXIO485 *bus = _busList; bus != NULL; bus = bus->getNext()) { if (bus->getBusNumber() == busNo) return bus; } @@ -552,4 +643,4 @@ uint16_t crc16(uint8_t *data, uint16_t length); }; -#endif // IO_RSproto_H \ No newline at end of file +#endif // IO_EXIO485_H \ No newline at end of file From 51058a66f3b9adbdade81df20fac1c4a5e2eeb86 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Thu, 26 Dec 2024 15:40:20 -0500 Subject: [PATCH 67/73] last working, full-duplex --- IO_EXIO485.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/IO_EXIO485.cpp b/IO_EXIO485.cpp index c44d6299..878736ce 100644 --- a/IO_EXIO485.cpp +++ b/IO_EXIO485.cpp @@ -87,6 +87,7 @@ void EXIO485::_loop(unsigned long currentMicros) { buffB[2] = (EXIORDAN); addTask(buffB, 3, EXIORDAN); _currentNode = _currentNode->getNext(); + DIAG(F("Polling")); } if ( hasTasks() && _currentMicros - _cycleStartTimeA >= _cycleTime){ @@ -111,7 +112,7 @@ void EXIO485::_loop(unsigned long currentMicros) { if (_txPin != -1) digitalWrite(_txPin,LOW); // delete task command after sending, for now currentTask->rxMode = true; - + DIAG(F("Task")); markTaskCompleted(currentTask->taskID); } } From 25c01e0ca419d1c589ac9eff08c4d4b908c9d33b Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Thu, 26 Dec 2024 17:21:02 -0500 Subject: [PATCH 68/73] update for better task handeling, untested --- IO_EXIO485.cpp | 294 ++++++++++++++++++++++++++----------------------- IO_EXIO485.h | 2 + 2 files changed, 160 insertions(+), 136 deletions(-) diff --git a/IO_EXIO485.cpp b/IO_EXIO485.cpp index 878736ce..19a05a25 100644 --- a/IO_EXIO485.cpp +++ b/IO_EXIO485.cpp @@ -90,9 +90,31 @@ void EXIO485::_loop(unsigned long currentMicros) { DIAG(F("Polling")); } - if ( hasTasks() && _currentMicros - _cycleStartTimeA >= _cycleTime){ + if ( hasTasks()){ _cycleStartTimeA = _currentMicros; - Task* currentTask = getTaskById(getNextTaskId()); + if (CurrentTaskID == -1) { + CurrentTaskID = getNextTaskId(); + + } + + Task* currentTask = getTaskById(CurrentTaskID); + if (_currentMicros - _cycleStartTime > 1000000UL) { // timout every 1000ms + _cycleStartTime = _currentMicros;// reset timout + if (taskResendCount >= 2) { // max resends + markTaskCompleted(CurrentTaskID); // kill task and move on + CurrentTaskID = getNextTaskId(); // move on + taskResendCount = 0; + DIAG(F("Move on")); + } else { + currentTask->rxMode = false; // resend + taskResendCount++; + DIAG(F("Resend")); + } + + } + + + if (!currentTask->rxMode) { // Check if a task was found currentTask->crcPassFail = 0; uint16_t response_crc = crc16((uint8_t*)currentTask->commandArray, currentTask->byteCount-1); @@ -113,154 +135,154 @@ void EXIO485::_loop(unsigned long currentMicros) { // delete task command after sending, for now currentTask->rxMode = true; DIAG(F("Task")); - markTaskCompleted(currentTask->taskID); - } - } - if ( _serial->available()) { - - - uint16_t calculated_crc; - int byteCount = 100; - - int curByte = _serial->read(); - - if (curByte == 0xFE && flagStart == false) flagStart = true; - else if ( curByte == 0xFE && flagStart == true) { - flagProc = false; - byteCounter = 0; - flagStarted = true; - flagStart = false; - flagEnded = false; - rxStart = true; - rxEnd = false; - crcPass = false; - memset(received_data, 0, ARRAY_SIZE); - }else if (flagStarted) { - crc[0] = curByte; - byteCounter++; - flagStarted = false; - } else if (byteCounter == 1) { - crc[1] = curByte; - received_crc = (crc[0] << 8) | crc[1]; - byteCounter++; - } else if (byteCounter == 2) { - byteCount = curByte; - byteCounter++; - } else if (flagEnded == false && byteCounter >= 3) { - received_data[byteCounter-3] = curByte; - byteCounter++; - } - if (curByte == 0xFD && flagEnd == false) flagEnd = true; - else if ( curByte == 0xFD && flagEnd == true) { - flagEnded = true; - flagEnd = false; - rxEnd = true; - byteCount = byteCounter; - byteCounter = 0; - } - if (flagEnded) { - calculated_crc = crc16((uint8_t*)received_data, byteCount-6); - if (received_crc == calculated_crc) { - crcPass = true; - } - flagEnded = false; + + } else { + if ( _serial->available()) { + + uint16_t calculated_crc; + int byteCount = 100; + + int curByte = _serial->read(); + + if (curByte == 0xFE && flagStart == false) flagStart = true; + else if ( curByte == 0xFE && flagStart == true) { + flagProc = false; + byteCounter = 0; + flagStarted = true; + flagStart = false; + flagEnded = false; + rxStart = true; + rxEnd = false; + crcPass = false; + memset(received_data, 0, ARRAY_SIZE); + }else if (flagStarted) { + crc[0] = curByte; + byteCounter++; + flagStarted = false; + } else if (byteCounter == 1) { + crc[1] = curByte; + received_crc = (crc[0] << 8) | crc[1]; + byteCounter++; + } else if (byteCounter == 2) { + byteCount = curByte; + byteCounter++; + } else if (flagEnded == false && byteCounter >= 3) { + received_data[byteCounter-3] = curByte; + byteCounter++; + } + if (curByte == 0xFD && flagEnd == false) flagEnd = true; + else if ( curByte == 0xFD && flagEnd == true) { + flagEnded = true; + flagEnd = false; + rxEnd = true; + byteCount = byteCounter; + byteCounter = 0; + } + if (flagEnded) { + calculated_crc = crc16((uint8_t*)received_data, byteCount-6); + if (received_crc == calculated_crc) { + crcPass = true; + } + flagEnded = false; + - } - // Check CRC validity - if (crcPass) { - // Data received successfully, process it (e.g., print) - int nodeTo = received_data[0]; - if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. - flagProc = true; - } - - } + } + // Check CRC validity + if (crcPass) { + // Data received successfully, process it (e.g., print) + int nodeTo = received_data[0]; + if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. + flagProc = true; + } + + } - if (flagProc) { - int nodeFr = received_data[1]; - EXIO485node *node = findNode(nodeFr); - int AddrCode = received_data[2]; - - switch (AddrCode) { - case EXIOPINS: - {node->setnumDigitalPins(received_data[3]); - node->setnumAnalogPins(received_data[4]); + if (flagProc) { + int nodeFr = received_data[1]; + EXIO485node *node = findNode(nodeFr); + int AddrCode = received_data[2]; + + switch (AddrCode) { + case EXIOPINS: + {node->setnumDigitalPins(received_data[3]); + node->setnumAnalogPins(received_data[4]); - // See if we already have suitable buffers assigned - if (node->getnumDigialPins()>0) { - size_t digitalBytesNeeded = (node->getnumDigialPins() + 7) / 8; - if (node->getdigitalPinBytes() < digitalBytesNeeded) { - // Not enough space, free any existing buffer and allocate a new one - if (node->cleandigitalPinStates(digitalBytesNeeded)) { - node->setdigitalPinBytes(digitalBytesNeeded); - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), nodeFr, digitalBytesNeeded); - node->setdigitalPinBytes(0); + // See if we already have suitable buffers assigned + if (node->getnumDigialPins()>0) { + size_t digitalBytesNeeded = (node->getnumDigialPins() + 7) / 8; + if (node->getdigitalPinBytes() < digitalBytesNeeded) { + // Not enough space, free any existing buffer and allocate a new one + if (node->cleandigitalPinStates(digitalBytesNeeded)) { + node->setdigitalPinBytes(digitalBytesNeeded); + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), nodeFr, digitalBytesNeeded); + node->setdigitalPinBytes(0); + } + } } - } - } - - if (node->getnumAnalogPins()>0) { - size_t analogueBytesNeeded = node->getnumAnalogPins() * 2; - if (node->getanalogPinBytes() < analogueBytesNeeded) { - // Free any existing buffers and allocate new ones. - if (node->cleanAnalogStates(analogueBytesNeeded)) { - node->setanalogPinBytes(analogueBytesNeeded); - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), nodeFr); - node->setanalogPinBytes(0); + if (node->getnumAnalogPins()>0) { + size_t analogueBytesNeeded = node->getnumAnalogPins() * 2; + if (node->getanalogPinBytes() < analogueBytesNeeded) { + // Free any existing buffers and allocate new ones. + + if (node->cleanAnalogStates(analogueBytesNeeded)) { + node->setanalogPinBytes(analogueBytesNeeded); + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), nodeFr); + node->setanalogPinBytes(0); + } + } } + markTaskCompleted(currentTask->taskID); + break;} + case EXIOINITA: { + for (int i = 0; i < node->getnumAnalogPins(); i++) { + node->setanalogPinMap(received_data[i+3], i); + } + markTaskCompleted(currentTask->taskID); + break; + } + case EXIOVER: { + node->setMajVer(received_data[3]); + node->setMinVer(received_data[4]); + node->setPatVer(received_data[5]); + DIAG(F("EX-IOExpander485: Found node %i v%i.%i.%i"),node->getNodeID(), node->getMajVer(), node->getMinVer(), node->getPatVer()); + node->setInitialised(); + markTaskCompleted(currentTask->taskID); + break; } + case EXIORDY: { + markTaskCompleted(currentTask->taskID); + break; + } + case EXIOERR: { + markTaskCompleted(currentTask->taskID); + DIAG(F("EX-IOExplorer485: Some sort of error was received...")); // ;-) + break; + } + case EXIORDAN: { + for (int i = 0; i < node->_numAnaloguePins; i++) { + node->setanalogInputBuffer(received_data[i+3], i); + } + markTaskCompleted(currentTask->taskID); + break; + } + case EXIORDD: { + for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { + node->setdigitalInputStates(received_data[i+3], i); + } + markTaskCompleted(currentTask->taskID); + break; } - break;} - case EXIOINITA: { - for (int i = 0; i < node->getnumAnalogPins(); i++) { - node->setanalogPinMap(received_data[i+3], i); } - break; - } - case EXIOVER: { - node->setMajVer(received_data[3]); - node->setMinVer(received_data[4]); - node->setPatVer(received_data[5]); - DIAG(F("EX-IOExpander485: Found node %i v%i.%i.%i"),node->getNodeID(), node->getMajVer(), node->getMinVer(), node->getPatVer()); - node->setInitialised(); - break; - } - case EXIORDY: { - break; - } - case EXIOERR: { - DIAG(F("EX-IOExplorer485: Some sort of error was received...")); // ;-) - break; - } - case EXIORDAN: { - for (int i = 0; i < node->_numAnaloguePins; i++) { - node->setanalogInputBuffer(received_data[i+3], i); - } - - break; - } - case EXIORDD: { - for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { - node->setdigitalInputStates(received_data[i+3], i); + } - break; - } + flagProc = false; } - } - flagProc = false; } - - // temp debug - - - - - } // Link to chain of EXIO485 instances, left over from EXIO485 template. diff --git a/IO_EXIO485.h b/IO_EXIO485.h index 7974fc5f..9fc6eb2a 100644 --- a/IO_EXIO485.h +++ b/IO_EXIO485.h @@ -428,6 +428,8 @@ struct Task { }; static const int MAX_TASKS = 50; int taskIDCntr = 0; + int CurrentTaskID = -1; + int taskResendCount = 0; Task taskBuffer[MAX_TASKS]; // Buffer to hold up to 100 tasks int currentTaskIndex = 0; void initTask() { From 495c9407a873fa23ca2925c1f63f26659d98aa99 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sat, 28 Dec 2024 02:40:50 -0500 Subject: [PATCH 69/73] works --- IO_EXIO485.cpp | 210 +++++++++++++++++++++++-------------------------- IO_EXIO485.h | 26 +++--- 2 files changed, 116 insertions(+), 120 deletions(-) diff --git a/IO_EXIO485.cpp b/IO_EXIO485.cpp index 19a05a25..ec4a96ec 100644 --- a/IO_EXIO485.cpp +++ b/IO_EXIO485.cpp @@ -75,7 +75,6 @@ void EXIO485::_loop(unsigned long currentMicros) { _currentMicros = currentMicros; if (_currentNode == NULL) _currentNode = _nodeListStart; if (!hasTasks() && _currentNode->isInitialised()) { - _cycleStartTime = _currentMicros; uint8_t buffA[3]; buffA[0] = (_currentNode->getNodeID()); buffA[1] = (0); @@ -87,38 +86,24 @@ void EXIO485::_loop(unsigned long currentMicros) { buffB[2] = (EXIORDAN); addTask(buffB, 3, EXIORDAN); _currentNode = _currentNode->getNext(); - DIAG(F("Polling")); + //DIAG(F("Polling")); } if ( hasTasks()){ _cycleStartTimeA = _currentMicros; - if (CurrentTaskID == -1) { - CurrentTaskID = getNextTaskId(); - - } - - Task* currentTask = getTaskById(CurrentTaskID); - if (_currentMicros - _cycleStartTime > 1000000UL) { // timout every 1000ms - _cycleStartTime = _currentMicros;// reset timout - if (taskResendCount >= 2) { // max resends - markTaskCompleted(CurrentTaskID); // kill task and move on - CurrentTaskID = getNextTaskId(); // move on - taskResendCount = 0; - DIAG(F("Move on")); - } else { - currentTask->rxMode = false; // resend - taskResendCount++; - DIAG(F("Resend")); - } - - } + if (CurrentTaskID == -1) CurrentTaskID = getNextTaskId(); + Task* currentTask = getTaskById(CurrentTaskID); + //if (currentTask == nullptr) return; + if (!currentTask->rxMode) { // Check if a task was found currentTask->crcPassFail = 0; uint16_t response_crc = crc16((uint8_t*)currentTask->commandArray, currentTask->byteCount-1); - if (_txPin != -1) digitalWrite(_txPin,HIGH); + //delayUntil(_currentMicros+10000UL); + + ArduinoPins::fastWriteDigital(_txPin,HIGH); // Send response data with CRC _serial->write(0xFE); _serial->write(0xFE); @@ -131,18 +116,13 @@ void EXIO485::_loop(unsigned long currentMicros) { _serial->write(0xFD); _serial->write(0xFD); _serial->flush(); - if (_txPin != -1) digitalWrite(_txPin,LOW); + ArduinoPins::fastWriteDigital(_txPin,LOW); // delete task command after sending, for now currentTask->rxMode = true; - DIAG(F("Task")); + //DIAG(F("Task %d"), currentTask->taskID); } else { if ( _serial->available()) { - - - uint16_t calculated_crc; - int byteCount = 100; - int curByte = _serial->read(); if (curByte == 0xFE && flagStart == false) flagStart = true; @@ -188,99 +168,110 @@ void EXIO485::_loop(unsigned long currentMicros) { } - // Check CRC validity - if (crcPass) { - // Data received successfully, process it (e.g., print) - int nodeTo = received_data[0]; - if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. - flagProc = true; - } - + } + // Check CRC validity + if (crcPass) { + // Data received successfully, process it (e.g., print) + int nodeTo = received_data[0]; + if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. + flagProc = true; } + + } + + if (flagProc) { + crcPass = false; + int nodeFr = received_data[1]; + EXIO485node *node = findNode(nodeFr); + int AddrCode = received_data[2]; + + switch (AddrCode) { + case EXIOPINS: + {node->setnumDigitalPins(received_data[3]); + node->setnumAnalogPins(received_data[4]); - if (flagProc) { - int nodeFr = received_data[1]; - EXIO485node *node = findNode(nodeFr); - int AddrCode = received_data[2]; - - switch (AddrCode) { - case EXIOPINS: - {node->setnumDigitalPins(received_data[3]); - node->setnumAnalogPins(received_data[4]); - - // See if we already have suitable buffers assigned - if (node->getnumDigialPins()>0) { - size_t digitalBytesNeeded = (node->getnumDigialPins() + 7) / 8; - if (node->getdigitalPinBytes() < digitalBytesNeeded) { - // Not enough space, free any existing buffer and allocate a new one - if (node->cleandigitalPinStates(digitalBytesNeeded)) { - node->setdigitalPinBytes(digitalBytesNeeded); - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), nodeFr, digitalBytesNeeded); - node->setdigitalPinBytes(0); - } + // See if we already have suitable buffers assigned + if (node->getnumDigialPins()>0) { + size_t digitalBytesNeeded = (node->getnumDigialPins() + 7) / 8; + if (node->getdigitalPinBytes() < digitalBytesNeeded) { + // Not enough space, free any existing buffer and allocate a new one + if (node->cleandigitalPinStates(digitalBytesNeeded)) { + node->setdigitalPinBytes(digitalBytesNeeded); + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc %d bytes"), nodeFr, digitalBytesNeeded); + node->setdigitalPinBytes(0); } } - - if (node->getnumAnalogPins()>0) { - size_t analogueBytesNeeded = node->getnumAnalogPins() * 2; - if (node->getanalogPinBytes() < analogueBytesNeeded) { - // Free any existing buffers and allocate new ones. - - if (node->cleanAnalogStates(analogueBytesNeeded)) { - node->setanalogPinBytes(analogueBytesNeeded); - } else { - DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), nodeFr); - node->setanalogPinBytes(0); - } + } + + if (node->getnumAnalogPins()>0) { + size_t analogueBytesNeeded = node->getnumAnalogPins() * 2; + if (node->getanalogPinBytes() < analogueBytesNeeded) { + // Free any existing buffers and allocate new ones. + + if (node->cleanAnalogStates(analogueBytesNeeded)) { + node->setanalogPinBytes(analogueBytesNeeded); + } else { + DIAG(F("EX-IOExpander485 node:%d ERROR alloc analog pin bytes"), nodeFr); + node->setanalogPinBytes(0); } } - markTaskCompleted(currentTask->taskID); - break;} - case EXIOINITA: { - for (int i = 0; i < node->getnumAnalogPins(); i++) { - node->setanalogPinMap(received_data[i+3], i); - } - markTaskCompleted(currentTask->taskID); - break; - } - case EXIOVER: { - node->setMajVer(received_data[3]); - node->setMinVer(received_data[4]); - node->setPatVer(received_data[5]); - DIAG(F("EX-IOExpander485: Found node %i v%i.%i.%i"),node->getNodeID(), node->getMajVer(), node->getMinVer(), node->getPatVer()); - node->setInitialised(); - markTaskCompleted(currentTask->taskID); - break; - } - case EXIORDY: { - markTaskCompleted(currentTask->taskID); - break; } - case EXIOERR: { - markTaskCompleted(currentTask->taskID); - DIAG(F("EX-IOExplorer485: Some sort of error was received...")); // ;-) - break; + markTaskCompleted(CurrentTaskID); + flagProc = false; + break;} + case EXIOINITA: { + for (int i = 0; i < node->getnumAnalogPins(); i++) { + node->setanalogPinMap(received_data[i+3], i); } - case EXIORDAN: { - for (int i = 0; i < node->_numAnaloguePins; i++) { - node->setanalogInputBuffer(received_data[i+3], i); - } - markTaskCompleted(currentTask->taskID); - break; - } - case EXIORDD: { - for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { - node->setdigitalInputStates(received_data[i+3], i); + + markTaskCompleted(CurrentTaskID); + flagProc = false; + break; + } + case EXIOVER: { + node->setMajVer(received_data[3]); + node->setMinVer(received_data[4]); + node->setPatVer(received_data[5]); + DIAG(F("EX-IOExpander485: Found node %d v%d.%d.%d"),node->getNodeID(), node->getMajVer(), node->getMinVer(), node->getPatVer()); + node->setInitialised(); + markTaskCompleted(CurrentTaskID); + flagProc = false; + break; + } + case EXIORDY: { + markTaskCompleted(CurrentTaskID); + flagProc = false; + break; + } + case EXIOERR: { + markTaskCompleted(CurrentTaskID); + DIAG(F("EX-IOExplorer485: Some sort of error was received...")); // ;-) + flagProc = false; + break; + } + case EXIORDAN: { + for (int i = 0; i < node->_numAnaloguePins; i++) { + node->setanalogInputBuffer(received_data[i+3], i); } - markTaskCompleted(currentTask->taskID); + markTaskCompleted(CurrentTaskID); + flagProc = false; break; } + case EXIORDD: { + for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { + node->setdigitalInputStates(received_data[i+3], i); + } + markTaskCompleted(CurrentTaskID); + flagProc = false; + break; } - } - flagProc = false; + + } + + } } } @@ -362,7 +353,6 @@ void EXIO485node::_begin() { buff[4] = ((_firstVpin & 0xFF)); buff[5] = ((_firstVpin >> 8)); EXIO485 *bus = EXIO485::findBus(0); - bus->initTask(); bus->setBusy(); bus->addTask(buff, 6, EXIOINIT); diff --git a/IO_EXIO485.h b/IO_EXIO485.h index 9fc6eb2a..6c3c9581 100644 --- a/IO_EXIO485.h +++ b/IO_EXIO485.h @@ -22,13 +22,13 @@ * EXIO485 * ======= * To define a EXIO485, example syntax: - * EXIO485::create(busNo, serial, baud[, pin]); + * EXIO485::create(busNo, serial, baud[, TxPin]); * * busNo = the Bus no of the instance. should = 0, unless more than one bus configured for some reason. * 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 EXIO485 module's DE and !RE terminals for half-duplex operation (default -1) + * TxPin = pin number connected to EXIO485 module's DE and !RE terminals for half-duplex operation (default -1) * if omitted (default), hardware MUST support full-duplex opperation! * * @@ -416,7 +416,7 @@ class EXIO485 : public IODevice { public: struct Task { static const int ARRAY_SIZE = 150; - int taskID; + long taskID; uint8_t commandArray[ARRAY_SIZE]; int byteCount; uint8_t retFlag; @@ -427,8 +427,8 @@ struct Task { bool processed; }; static const int MAX_TASKS = 50; - int taskIDCntr = 0; - int CurrentTaskID = -1; + long taskIDCntr = 1; + long CurrentTaskID = -1; int taskResendCount = 0; Task taskBuffer[MAX_TASKS]; // Buffer to hold up to 100 tasks int currentTaskIndex = 0; @@ -467,6 +467,9 @@ void addTask(const uint8_t* cmd, int byteCount, uint8_t retFlag) { taskBuffer[emptySlot].gotCallback = false; taskBuffer[emptySlot].completed = false; taskBuffer[emptySlot].processed = false; + taskIDCntr++; + if (taskIDCntr >= 5000000) taskIDCntr = 1; + taskBuffer[emptySlot].taskID = taskIDCntr; currentTaskIndex = emptySlot; } bool hasTasks() { @@ -487,7 +490,7 @@ Task* getTaskById(int id) { return nullptr; // Task not found } // Function to get the next task (optional) -int getNextTaskId() { +long getNextTaskId() { for (int i = 0; i < MAX_TASKS; i++) { if (!taskBuffer[i].completed) { return taskBuffer[i].taskID; @@ -499,7 +502,9 @@ int getNextTaskId() { void markTaskCompleted(int id) { for (int i = 0; i < MAX_TASKS; i++) { if (taskBuffer[i].taskID == id) { - taskBuffer[i].completed = true; + taskBuffer[i].completed = true; // completed + taskBuffer[i].taskID = -1; // unassigned + CurrentTaskID = getNextTaskId(); break; } } @@ -512,13 +517,13 @@ bool rxStart = false; bool rxEnd = false; bool crcPass = false; bool flagProc = false; +uint16_t calculated_crc; + int byteCount = 100; uint8_t received_data[ARRAY_SIZE]; uint16_t received_crc; uint8_t crc[2]; uint16_t crc16(uint8_t *data, uint16_t length); - void remove_nulls(char *str, int len); - int getCharsLeft(char *str, char position); - void parseRx(uint8_t * outArray, uint8_t retFlag); + // EX-IOExpander protocol flags enum { EXIOINIT = 0xE0, // Flag to initialise setup procedure @@ -562,6 +567,7 @@ uint16_t crc16(uint8_t *data, uint16_t length); unsigned long taskCounter=0ul; // Device-specific initialisation void _begin() override { + //initTask(); _serial->begin(_baud, SERIAL_8N1); if (_txPin >0) { pinMode(_txPin, OUTPUT); From 23eb35290f3b07db8bdc9ddf1047e38327a76c22 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sat, 28 Dec 2024 03:00:23 -0500 Subject: [PATCH 70/73] code cleaned --- IO_EXIO485.cpp | 53 ++++++-------- IO_EXIO485.h | 188 ++++++++++++++++++++----------------------------- 2 files changed, 98 insertions(+), 143 deletions(-) diff --git a/IO_EXIO485.cpp b/IO_EXIO485.cpp index ec4a96ec..0c0b4ac9 100644 --- a/IO_EXIO485.cpp +++ b/IO_EXIO485.cpp @@ -1,6 +1,5 @@ /* * © 2024, Travis Farmer. All rights reserved. - * © 2021 Chris Harlow * * This file is part of DCC++EX API * @@ -48,14 +47,7 @@ EXIO485::EXIO485(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8 _busList = this; } -/* -= _loop =- -// -// Main loop function for EXIO485. -// Work through list of nodes. For each node, in separate loop entries -// When the slot time has finished, move on to the next device. -*/ - -// CRC-16 implementation (replace with your preferred CRC library if needed) +// CRC-16 implementation uint16_t EXIO485::crc16(uint8_t *data, uint16_t length) { uint16_t crc = 0xFFFF; for (uint16_t i = 0; i < length; i++) { @@ -71,10 +63,18 @@ uint16_t EXIO485::crc16(uint8_t *data, uint16_t length) { return crc; } + +/* -= _loop =- +// +// Main loop function for EXIO485. +// 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 EXIO485::_loop(unsigned long currentMicros) { _currentMicros = currentMicros; if (_currentNode == NULL) _currentNode = _nodeListStart; - if (!hasTasks() && _currentNode->isInitialised()) { + if (!hasTasks() && _currentNode->isInitialised()) { // no tasks? lets poll for data uint8_t buffA[3]; buffA[0] = (_currentNode->getNodeID()); buffA[1] = (0); @@ -86,24 +86,19 @@ void EXIO485::_loop(unsigned long currentMicros) { buffB[2] = (EXIORDAN); addTask(buffB, 3, EXIORDAN); _currentNode = _currentNode->getNext(); - //DIAG(F("Polling")); } - if ( hasTasks()){ + if ( hasTasks()){ // do we have any tasks on the docket _cycleStartTimeA = _currentMicros; if (CurrentTaskID == -1) CurrentTaskID = getNextTaskId(); Task* currentTask = getTaskById(CurrentTaskID); - //if (currentTask == nullptr) return; - - - if (!currentTask->rxMode) { // Check if a task was found + if (!currentTask->rxMode) { currentTask->crcPassFail = 0; uint16_t response_crc = crc16((uint8_t*)currentTask->commandArray, currentTask->byteCount-1); - //delayUntil(_currentMicros+10000UL); - ArduinoPins::fastWriteDigital(_txPin,HIGH); + if (_txPin != -1) ArduinoPins::fastWriteDigital(_txPin,HIGH); // Send response data with CRC _serial->write(0xFE); _serial->write(0xFE); @@ -116,10 +111,9 @@ void EXIO485::_loop(unsigned long currentMicros) { _serial->write(0xFD); _serial->write(0xFD); _serial->flush(); - ArduinoPins::fastWriteDigital(_txPin,LOW); + if (_txPin != -1) ArduinoPins::fastWriteDigital(_txPin,LOW); // delete task command after sending, for now currentTask->rxMode = true; - //DIAG(F("Task %d"), currentTask->taskID); } else { if ( _serial->available()) { @@ -165,18 +159,15 @@ void EXIO485::_loop(unsigned long currentMicros) { crcPass = true; } flagEnded = false; - - } } // Check CRC validity if (crcPass) { // Data received successfully, process it (e.g., print) int nodeTo = received_data[0]; - if (nodeTo == 0) { // for master. master does not retransmit, or a loop will runaway. + if (nodeTo == 0) { // for master. flagProc = true; } - } if (flagProc) { @@ -259,19 +250,15 @@ void EXIO485::_loop(unsigned long currentMicros) { break; } case EXIORDD: { - for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { - node->setdigitalInputStates(received_data[i+3], i); - } - markTaskCompleted(CurrentTaskID); - flagProc = false; + for (int i = 0; i < (node->_numDigitalPins+7)/8; i++) { + node->setdigitalInputStates(received_data[i+3], i); + } + markTaskCompleted(CurrentTaskID); + flagProc = false; break; } } - - } - - } } } diff --git a/IO_EXIO485.h b/IO_EXIO485.h index 6c3c9581..8d52e283 100644 --- a/IO_EXIO485.h +++ b/IO_EXIO485.h @@ -1,6 +1,5 @@ /* * © 2024, Travis Farmer. All rights reserved. -* © 2021 Chris Harlow * * This file is part of DCC++EX API * @@ -39,7 +38,7 @@ * * firstVPIN = first vpin in block allocated to this device * numVPINs = number of vpins - * nodeID = 1-251 + * nodeID = 1-252 */ #ifndef IO_EXIO485_H @@ -382,7 +381,7 @@ class EXIO485 : public IODevice { byte bufferLength; static const int ARRAY_SIZE = 150; int buffer[ARRAY_SIZE]; - byte inCommandPayload; + byte inCommandPayload; static EXIO485 *_busList; // linked list of defined bus instances bool waitReceive = false; int _waitCounter = 0; @@ -406,12 +405,6 @@ class EXIO485 : public IODevice { uint16_t _receiveDataIndex = 0; // Index of next data byte to be received. EXIO485 *_nextBus = NULL; // Pointer to next bus instance in list. -// 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; - } int byteCounter = 0; public: struct Task { @@ -426,103 +419,91 @@ struct Task { bool completed; bool processed; }; -static const int MAX_TASKS = 50; + static const int MAX_TASKS = 50; long taskIDCntr = 1; long CurrentTaskID = -1; int taskResendCount = 0; -Task taskBuffer[MAX_TASKS]; // Buffer to hold up to 100 tasks -int currentTaskIndex = 0; -void initTask() { - for (int i = 0; i = 5000000) taskIDCntr = 1; - taskBuffer[emptySlot].taskID = taskIDCntr; - currentTaskIndex = emptySlot; -} -bool hasTasks() { - for (int i = 0; i < MAX_TASKS; i++) { - if (!taskBuffer[i].completed) { - return true; // At least one task is not completed + // If no empty slot found, return (buffer full) + if (emptySlot == -1) { + DIAG(F("Task Buffer Full!")); + return; } - } - return false; // All tasks are completed -} -// Function to get a specific task by ID -Task* getTaskById(int id) { - for (int i = 0; i < MAX_TASKS; i++) { - if (taskBuffer[i].taskID == id) { - return &taskBuffer[i]; // Return a pointer to the task + for (int i = 0; i < byteCount; i++) taskBuffer[emptySlot].commandArray[i] = cmd[i]; + taskBuffer[emptySlot].byteCount = byteCount; + taskBuffer[emptySlot].retFlag = retFlag; + taskBuffer[emptySlot].rxMode = false; + taskBuffer[emptySlot].crcPassFail = 0; + taskBuffer[emptySlot].gotCallback = false; + taskBuffer[emptySlot].completed = false; + taskBuffer[emptySlot].processed = false; + taskIDCntr++; + if (taskIDCntr >= 5000000) taskIDCntr = 1; + taskBuffer[emptySlot].taskID = taskIDCntr; + currentTaskIndex = emptySlot; + } + bool hasTasks() { + for (int i = 0; i < MAX_TASKS; i++) { + if (!taskBuffer[i].completed) { + return true; // At least one task is not completed + } } - } - return nullptr; // Task not found -} -// Function to get the next task (optional) -long getNextTaskId() { - for (int i = 0; i < MAX_TASKS; i++) { - if (!taskBuffer[i].completed) { - return taskBuffer[i].taskID; + return false; // All tasks are completed + } + // Function to get a specific task by ID + Task* getTaskById(int id) { + for (int i = 0; i < MAX_TASKS; i++) { + if (taskBuffer[i].taskID == id) { + return &taskBuffer[i]; // Return a pointer to the task + } } - } - return -1; // No tasks available -} -// Function to mark a task as completed -void markTaskCompleted(int id) { - for (int i = 0; i < MAX_TASKS; i++) { - if (taskBuffer[i].taskID == id) { - taskBuffer[i].completed = true; // completed - taskBuffer[i].taskID = -1; // unassigned - CurrentTaskID = getNextTaskId(); - break; + return nullptr; // Task not found + } + // Function to get the next task (optional) + long getNextTaskId() { + for (int i = 0; i < MAX_TASKS; i++) { + if (!taskBuffer[i].completed) { + return taskBuffer[i].taskID; + } + } + return -1; // No tasks available + } + // Function to mark a task as completed + void markTaskCompleted(int id) { + for (int i = 0; i < MAX_TASKS; i++) { + if (taskBuffer[i].taskID == id) { + taskBuffer[i].completed = true; // completed + taskBuffer[i].taskID = -1; // unassigned + CurrentTaskID = getNextTaskId(); + break; + } } } -} -bool flagEnd = false; -bool flagEnded = false; -bool flagStart = false; -bool flagStarted = false; -bool rxStart = false; -bool rxEnd = false; -bool crcPass = false; -bool flagProc = false; -uint16_t calculated_crc; - int byteCount = 100; -uint8_t received_data[ARRAY_SIZE]; -uint16_t received_crc; -uint8_t crc[2]; -uint16_t crc16(uint8_t *data, uint16_t length); + bool flagEnd = false; + bool flagEnded = false; + bool flagStart = false; + bool flagStarted = false; + bool rxStart = false; + bool rxEnd = false; + bool crcPass = false; + bool flagProc = false; + uint16_t calculated_crc; + int byteCount = 100; + uint8_t received_data[ARRAY_SIZE]; + uint16_t received_crc; + uint8_t crc[2]; + uint16_t crc16(uint8_t *data, uint16_t length); // EX-IOExpander protocol flags enum { @@ -567,25 +548,12 @@ uint16_t crc16(uint8_t *data, uint16_t length); unsigned long taskCounter=0ul; // Device-specific initialisation void _begin() override { - //initTask(); _serial->begin(_baud, SERIAL_8N1); if (_txPin >0) { pinMode(_txPin, OUTPUT); digitalWrite(_txPin, LOW); } - #if defined(EXIO485_STM_OK) - pinMode(EXIO485_STM_OK, OUTPUT); - ArduinoPins::fastWriteDigital(EXIO485_STM_OK,LOW); - #endif - #if defined(EXIO485_STM_FAIL) - pinMode(EXIO485_STM_FAIL, OUTPUT); - ArduinoPins::fastWriteDigital(EXIO485_STM_FAIL,LOW); - #endif - #if defined(EXIO485_STM_COMM) - pinMode(EXIO485_STM_COMM, OUTPUT); - ArduinoPins::fastWriteDigital(EXIO485_STM_COMM,LOW); - #endif #if defined(DIAG_IO) _display(); From a6fcad2ecf0dc7fdd5d92730db6cb49f9ef7c738 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sat, 28 Dec 2024 03:26:03 -0500 Subject: [PATCH 71/73] increased task buffer size --- IO_EXIO485.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IO_EXIO485.h b/IO_EXIO485.h index 8d52e283..b635fb96 100644 --- a/IO_EXIO485.h +++ b/IO_EXIO485.h @@ -419,7 +419,7 @@ struct Task { bool completed; bool processed; }; - static const int MAX_TASKS = 50; + static const int MAX_TASKS = 1000; long taskIDCntr = 1; long CurrentTaskID = -1; int taskResendCount = 0; From 764380ae5791d04375cc4db9f9cf9665176c4c4c Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sun, 29 Dec 2024 06:02:06 -0500 Subject: [PATCH 72/73] save current, works for now --- IO_EXIO485.cpp | 21 +++++++++++++++++---- IO_EXIO485.h | 15 +++++++++------ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/IO_EXIO485.cpp b/IO_EXIO485.cpp index 0c0b4ac9..21eff9f6 100644 --- a/IO_EXIO485.cpp +++ b/IO_EXIO485.cpp @@ -30,13 +30,13 @@ static const byte PAYLOAD_STRING = 2; ************************************************************/ // Constructor for EXIO485 -EXIO485::EXIO485(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin, int cycleTime) { +EXIO485::EXIO485(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin) { _serial = &serial; _baud = baud; _txPin = txPin; _busNo = busNo; - _cycleTime = cycleTime * 1000UL; + _retryTime = 1000000UL; // 1 second bufferLength=0; inCommandPayload=PAYLOAD_FALSE; // Add device to HAL device chain @@ -89,10 +89,21 @@ void EXIO485::_loop(unsigned long currentMicros) { } if ( hasTasks()){ // do we have any tasks on the docket - _cycleStartTimeA = _currentMicros; + if (CurrentTaskID == -1) CurrentTaskID = getNextTaskId(); - Task* currentTask = getTaskById(CurrentTaskID); + if (currentTask == nullptr) return; // dead task + + if (!currentTask->completed && _currentMicros - _cycleStartTimeA >= _retryTime) { // after CRC pass, timer is reset + + if (currentTask->currentRetryTimer == 0UL) { // first trigger + currentTask->currentRetryTimer = _currentMicros; // set timer + } else if (_currentMicros - currentTask->currentRetryTimer >= _retryTime) { + currentTask->currentRetryTimer = _currentMicros; // reset timer + currentTask->rxMode = false; // resend data + DIAG(F("EX-IOExplorer485: Fail RX, Retry TX. Task: %d"), CurrentTaskID); + } + } if (!currentTask->rxMode) { currentTask->crcPassFail = 0; @@ -167,6 +178,7 @@ void EXIO485::_loop(unsigned long currentMicros) { int nodeTo = received_data[0]; if (nodeTo == 0) { // for master. flagProc = true; + } } @@ -258,6 +270,7 @@ void EXIO485::_loop(unsigned long currentMicros) { break; } } + _cycleStartTimeA = currentMicros; // reset timer so we do not resend data } } } diff --git a/IO_EXIO485.h b/IO_EXIO485.h index b635fb96..52b1f096 100644 --- a/IO_EXIO485.h +++ b/IO_EXIO485.h @@ -371,7 +371,7 @@ class EXIO485 : public IODevice { unsigned long _cycleStartTime = 0; unsigned long _cycleStartTimeA = 0; unsigned long _timeoutStart = 0; - unsigned long _cycleTime; // target time between successive read/write cycles, microseconds + unsigned long _retryTime; // target time between successive read/write cycles, microseconds unsigned long _timeoutPeriod; // timeout on read responses, in microseconds. unsigned long _currentMicros; // last value of micros() from _loop function. unsigned long _postDelay; // delay time after transmission before switching off transmitter (in us) @@ -418,12 +418,13 @@ struct Task { int crcPassFail; bool completed; bool processed; + unsigned long currentRetryTimer; }; - static const int MAX_TASKS = 1000; + static const int MAX_TASKS = 100; // we don't want to run out of task slots, but memory? long taskIDCntr = 1; long CurrentTaskID = -1; int taskResendCount = 0; - Task taskBuffer[MAX_TASKS]; // Buffer to hold up to 100 tasks + Task taskBuffer[MAX_TASKS]; int currentTaskIndex = 0; void addTask(const uint8_t* cmd, int byteCount, uint8_t retFlag) { @@ -448,6 +449,7 @@ struct Task { taskBuffer[emptySlot].gotCallback = false; taskBuffer[emptySlot].completed = false; taskBuffer[emptySlot].processed = false; + taskBuffer[emptySlot].currentRetryTimer = 0UL; taskIDCntr++; if (taskIDCntr >= 5000000) taskIDCntr = 1; taskBuffer[emptySlot].taskID = taskIDCntr; @@ -485,6 +487,7 @@ struct Task { if (taskBuffer[i].taskID == id) { taskBuffer[i].completed = true; // completed taskBuffer[i].taskID = -1; // unassigned + taskBuffer[i].currentRetryTimer = 0UL; // stop timer CurrentTaskID = getNextTaskId(); break; } @@ -520,8 +523,8 @@ struct Task { EXIOWRAN = 0xEA, // Flag we're sending an analogue write (PWM) EXIOERR = 0xEF, // Flag we've received an error }; - static void create(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin=-1, int cycleTime=500) { - new EXIO485(busNo, serial, baud, txPin, cycleTime); + static void create(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin=-1) { + new EXIO485(busNo, serial, baud, txPin); } HardwareSerial* _serial; int _CommMode = 0; @@ -599,7 +602,7 @@ struct Task { } protected: - EXIO485(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin, int cycleTime); + EXIO485(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8_t txPin); public: From cc80b8cd1931b66ee85ecd94b87150e4d1fe7e88 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Tue, 31 Dec 2024 12:27:19 -0500 Subject: [PATCH 73/73] saving, works for now... --- IO_EXIO485.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IO_EXIO485.cpp b/IO_EXIO485.cpp index 21eff9f6..ebedc579 100644 --- a/IO_EXIO485.cpp +++ b/IO_EXIO485.cpp @@ -36,7 +36,7 @@ EXIO485::EXIO485(uint8_t busNo, HardwareSerial &serial, unsigned long baud, int8 _txPin = txPin; _busNo = busNo; - _retryTime = 1000000UL; // 1 second + _retryTime = 2000000UL; // 1 second bufferLength=0; inCommandPayload=PAYLOAD_FALSE; // Add device to HAL device chain