From b39523bfa6afbf3a557c4de5d9341e4e4cd62789 Mon Sep 17 00:00:00 2001 From: travis-farmer Date: Sat, 23 Nov 2024 16:15:36 -0500 Subject: [PATCH 01/36] 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 0000000..4c7df51 --- /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 0000000..4933eeb --- /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 0000000..b7752b2 --- /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 0000000..2aa72d9 --- /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 0000000..2abbe96 --- /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 0000000..cae7234 --- /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 0000000..897ce5a --- /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 0000000..d9193c3 --- /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/36] 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 4c7df51..7404b12 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 4933eeb..be5b65b 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 b7752b2..0000000 --- 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 2aa72d9..0000000 --- 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 2abbe96..0000000 --- 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 cae7234..0000000 --- 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 897ce5a..0000000 --- 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 d9193c3..0000000 --- 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/36] 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 be5b65b..2e929f2 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -238,7 +238,7 @@ public: 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/36] 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 7404b12..91891d1 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/36] 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 91891d1..8abe4d8 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 2e929f2..165225e 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -322,8 +322,9 @@ public: 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/36] 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 8abe4d8..888b470 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 165225e..7590e87 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -191,14 +191,18 @@ private: 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 @@ public: uint8_t getNumDisInputs() { return numDiscreteInputs; } - + uint8_t getNumHoldingRegisters() { + return numHoldingRegisters; + } + uint8_t getNumInputRegisters() { + return numInputRegisters; + } Modbusnode *getNext() { return _next; } @@ -227,7 +236,7 @@ public: _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 @@ public: 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 @@ public: } } + 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 @@ private: 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 @@ public: } 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/36] dereference errorStrings? --- IO_Modbus.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 888b470..7703442 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/36] move errorStrings --- IO_Modbus.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 7703442..259429f 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/36] fix comment --- IO_Modbus.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 259429f..02e8b37 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/36] 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 02e8b37..757e3ce 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 7590e87..6cd8271 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -210,7 +210,7 @@ public: 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/36] 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 6cd8271..004d82e 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -247,13 +247,13 @@ public: 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 @@ public: } 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/36] 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 757e3ce..01bbc4a 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 004d82e..ee06d55 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -345,7 +345,6 @@ public: 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/36] 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 01bbc4a..876590a 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 ee06d55..8d6b940 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -339,21 +339,14 @@ private: 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/36] 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 876590a..52b9331 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 8d6b940..262900c 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -189,16 +189,16 @@ private: 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 @@ public: 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 @@ public: _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 @@ public: 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 @@ public: } } - 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 @@ public: } 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 @@ public: } 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 @@ private: 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 @@ private: 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 @@ public: _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/36] 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 52b9331..8968dcd 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/36] 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 8968dcd..d9f37a1 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 262900c..a1df45e 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 @@ public: 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 @@ public: // 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: // 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/36] 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 d9f37a1..79279ae 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 a1df45e..d5a0bbc 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -240,7 +240,7 @@ public: // 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/36] 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 79279ae..7942bb7 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 d5a0bbc..6e64053 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -199,10 +199,10 @@ public: 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 @@ private: uint8_t _busNo; unsigned long _baud; - int8_t _transmitEnablePin; Modbusnode *_nodeListStart = NULL, *_nodeListEnd = NULL; Modbusnode *_currentNode = NULL; @@ -319,20 +318,21 @@ private: 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 @@ public: // 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 @@ public: } 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/36] seems to work, but need to test on hardware --- IO_Modbus.cpp | 75 ++++++++++++++++++++++++--------------------------- IO_Modbus.h | 72 ++++++++++++++++++++++++------------------------- 2 files changed, 71 insertions(+), 76 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 7942bb7..160236c 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 6e64053..a8313ea 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 @@ private: 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 @@ private: 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 @@ public: } 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/36] 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 160236c..8127c0e 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 a8313ea..4b0b564 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -229,15 +229,10 @@ public: 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 @@ public: } } + 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 @@ public: } } - 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 @@ public: 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/36] 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 4b0b564..40d4674 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/36] IO_Modbus: add status LEDs, STM32 --- IO_Modbus.cpp | 27 +++++++++++++++++++++++---- IO_Modbus.h | 9 ++++++++- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index 8127c0e..bbd6fdf 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 40d4674..786c92b 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -336,7 +336,14 @@ public: 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/36] hardware test shows VPINs are off by one --- IO_Modbus.cpp | 32 +++++++++++++++++++------------- IO_Modbus.h | 24 +++++++++++++++--------- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/IO_Modbus.cpp b/IO_Modbus.cpp index bbd6fdf..ad065e4 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); - ArduinoPins::fastWriteDigital(MODBUS_STM_FAIL,HIGH); } - ArduinoPins::fastWriteDigital(MODBUS_STM_TICK,LOW); +#endif +#if defined(MODBUS_STM_FAIL) + if (flagOK == false) { + ArduinoPins::fastWriteDigital(MODBUS_STM_FAIL,HIGH); + } else { + ArduinoPins::fastWriteDigital(MODBUS_STM_FAIL,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 786c92b..01b87e6 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -219,10 +219,11 @@ public: // 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 @@ public: 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 @@ public: 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); - ArduinoPins::fastWriteDigital(MODBUS_STM_FAIL,LOW); - ArduinoPins::fastWriteDigital(MODBUS_STM_TICK,LOW); #endif + #if defined(MODBUS_STM_FAIL) + pinMode(MODBUS_STM_FAIL, OUTPUT); + ArduinoPins::fastWriteDigital(MODBUS_STM_FAIL,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/36] 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 ad065e4..17e0878 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 01b87e6..55fba17 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -208,41 +208,26 @@ public: 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/36] 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 17e0878..6ff0ce0 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 55fba17..5539808 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -168,10 +168,10 @@ public: 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 @@ public: 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 @@ private: 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 @@ public: #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/36] 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 6ff0ce0..2a51955 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/36] 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 2a51955..887bf44 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 5539808..08db7a5 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 @@ private: 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/36] 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 887bf44..dff3bf9 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 08db7a5..a1a00c1 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -315,6 +315,10 @@ private: 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/36] 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 dff3bf9..d1fd185 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 a1a00c1..8590165 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -316,6 +316,8 @@ private: 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/36] 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 8590165..5b08f5c 100644 --- a/IO_Modbus.h +++ b/IO_Modbus.h @@ -228,7 +228,7 @@ public: 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 @@ private: 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/36] 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 d1fd185..c13658b 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 5b08f5c..1a42c16 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 @@ private: 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 @@ public: } 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/36] 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 c13658b..74929f9 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/36] 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 74929f9..4e9da46 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/36] 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 4e9da46..0ecb098 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 1a42c16..203929d 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 @@ public: 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/36] 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 0ecb098..1da5223 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 203929d..070365e 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 @@ public: 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/36] 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 1da5223..f7b0b22 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 070365e..d0f4779 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 @@ public: } }; -#endif + #endif // IO_MODBUS_H