diff --git a/src/ModbusMaster.cpp b/src/ModbusMaster.cpp index 4169e58..afcca2c 100644 --- a/src/ModbusMaster.cpp +++ b/src/ModbusMaster.cpp @@ -46,8 +46,15 @@ ModbusMaster::ModbusMaster(void) _idle = 0; _preTransmission = 0; _postTransmission = 0; + ku8MaxBufferSize=ku8DefMaxBufferSize; + _u16ResponseBuffer=_u16DefResponseBuffer; + } +#ifndef debugSerialPort +#define debugSerialPort Serial +#endif + /** Initialize class object. @@ -100,7 +107,7 @@ uint8_t ModbusMaster::requestFrom(uint16_t address, uint16_t quantity) void ModbusMaster::sendBit(bool data) { uint8_t txBitIndex = u16TransmitBufferLength % 16; - if ((u16TransmitBufferLength >> 4) < ku8MaxBufferSize) + if ((u16TransmitBufferLength >> 4) < ku8DefMaxBufferSize) { if (0 == txBitIndex) { @@ -115,7 +122,7 @@ void ModbusMaster::sendBit(bool data) void ModbusMaster::send(uint16_t data) { - if (_u8TransmitBufferIndex < ku8MaxBufferSize) + if (_u8TransmitBufferIndex < ku8DefMaxBufferSize) { _u16TransmitBuffer[_u8TransmitBufferIndex++] = data; u16TransmitBufferLength = _u8TransmitBufferIndex << 4; @@ -266,7 +273,7 @@ Place data in transmit buffer. */ uint8_t ModbusMaster::setTransmitBuffer(uint8_t u8Index, uint16_t u16Value) { - if (u8Index < ku8MaxBufferSize) + if (u8Index < ku8DefMaxBufferSize) { _u16TransmitBuffer[u8Index] = u16Value; return ku8MBSuccess; @@ -288,7 +295,7 @@ void ModbusMaster::clearTransmitBuffer() { uint8_t i; - for (i = 0; i < ku8MaxBufferSize; i++) + for (i = 0; i < ku8DefMaxBufferSize; i++) { _u16TransmitBuffer[i] = 0; } @@ -709,13 +716,34 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction) { _preTransmission(); } + + #ifdef MODBUS_DEBUG + debugSerialPort.println(); + #endif + for (i = 0; i < u8ModbusADUSize; i++) { _serial->write(u8ModbusADU[i]); + + #ifdef MODBUS_DEBUG + if (u8ModbusADU[i]<15) debugSerialPort.print("0"); + debugSerialPort.print (u8ModbusADU[i],HEX); + debugSerialPort.print(">"); + #endif + } + #ifdef MODBUS_DEBUG + debugSerialPort.println(); + #endif + u8ModbusADUSize = 0; _serial->flush(); // flush transmit buffer + + /* while (_tx_buffer->_iHead != _tx_buffer->_iTail); //wait for transmit data to be sent + // Wait for transmission to complete + while ((_pUart->UART_SR & UART_SR_TXEMPTY) != UART_SR_TXEMPTY) + ;*/ if (_postTransmission) { _postTransmission(); @@ -726,12 +754,24 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction) while (u8BytesLeft && !u8MBStatus) { if (_serial->available()) - { + { uint8_t ch; #if __MODBUSMASTER_DEBUG__ digitalWrite(__MODBUSMASTER_DEBUG_PIN_A__, true); #endif - u8ModbusADU[u8ModbusADUSize++] = _serial->read(); - u8BytesLeft--; + ch = _serial->read(); + +#ifdef MODBUS_DEBUG + if (ch<15) debugSerialPort.print("0"); + debugSerialPort.print (ch,HEX); + debugSerialPort.print("<"); +#endif + + + if ((ch == _u8MBSlave) || u8ModbusADUSize) + { + u8ModbusADU[u8ModbusADUSize++]=ch; + u8BytesLeft--; + } #if __MODBUSMASTER_DEBUG__ digitalWrite(__MODBUSMASTER_DEBUG_PIN_A__, false); #endif @@ -744,6 +784,7 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction) if (_idle) { _idle(); + if (_serial->available()) continue; } #if __MODBUSMASTER_DEBUG__ digitalWrite(__MODBUSMASTER_DEBUG_PIN_B__, false); @@ -753,13 +794,6 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction) // evaluate slave ID, function code once enough bytes have been read if (u8ModbusADUSize == 5) { - // verify response is for correct Modbus slave - if (u8ModbusADU[0] != _u8MBSlave) - { - u8MBStatus = ku8MBInvalidSlaveID; - break; - } - // verify response is for correct Modbus function code (mask exception bit 7) if ((u8ModbusADU[1] & 0x7F) != u8MBFunction) { @@ -874,3 +908,162 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction) _u8ResponseBufferIndex = 0; return u8MBStatus; } + +/** +Modbus-like protocols transaction engine. +Sequence: + - calculate CRC + - transmit buffer over selected serial port + CRC + - wait for/retrieve response + - return status (success/exception) + +@param u8ModbusADU - pointer to buffer +@param u8ModbusADUSize - request size +@param u8BytesLeft - how many bytes to be collected back (include CRC) - should be less then buffer size +@return 0 on success; exception number on failure + +*/ +uint8_t ModbusMaster::ModbusRawTransaction(uint8_t *u8ModbusADU,uint8_t u8ModbusADUSize, uint8_t u8BytesLeft ) +{ + + uint16_t u16CRC; + uint32_t u32StartTime; + + uint8_t u8MBStatus = ku8MBSuccess; + u8ModbusADU[0] = _u8MBSlave; + // calculate CRC + u16CRC = 0xFFFF; + for (uint8_t i = 0; i < u8ModbusADUSize; i++) + { + u16CRC = crc16_update(u16CRC, u8ModbusADU[i]); + } + + // flush receive buffer before transmitting request + while (_serial->read() != -1); + + // transmit request + if (_preTransmission) + { + _preTransmission(); + } + + #ifdef MODBUS_DEBUG + debugSerialPort.println(); + #endif + + for (uint8_t i = 0; i < u8ModbusADUSize; i++) + { + _serial->write(u8ModbusADU[i]); + + #ifdef MODBUS_DEBUG + if (u8ModbusADU[i]<15) debugSerialPort.print("0"); + debugSerialPort.print (u8ModbusADU[i],HEX); + debugSerialPort.print(">"); + #endif + + } + _serial->write(lowByte(u16CRC)); + _serial->write(highByte(u16CRC)); + + #ifdef MODBUS_DEBUG + if (lowByte(u16CRC)<15) debugSerialPort.print("0"); + debugSerialPort.print (lowByte(u16CRC),HEX); + + if (highByte(u16CRC)<15) debugSerialPort.print("0"); + debugSerialPort.print (highByte(u16CRC),HEX); + debugSerialPort.print(">"); + + debugSerialPort.println(); + #endif + + + u8ModbusADUSize = 0; + _serial->flush(); // flush transmit buffer + if (_postTransmission) + { + _postTransmission(); + } + + // loop until we run out of time or bytes, or an error occurs + u32StartTime = millis(); + while (u8BytesLeft && !u8MBStatus) + { + if (_serial->available()) + { uint8_t ch; +#if __MODBUSMASTER_DEBUG__ + digitalWrite(__MODBUSMASTER_DEBUG_PIN_A__, true); +#endif + ch = _serial->read(); + +#ifdef MODBUS_DEBUG + if (ch<15) debugSerialPort.print("0"); + debugSerialPort.print (ch,HEX); + debugSerialPort.print("<"); +#endif + + + if ((ch == _u8MBSlave) || u8ModbusADUSize) + { + u8ModbusADU[u8ModbusADUSize++]=ch; + u8BytesLeft--; + } +#if __MODBUSMASTER_DEBUG__ + digitalWrite(__MODBUSMASTER_DEBUG_PIN_A__, false); +#endif + } + else + { +#if __MODBUSMASTER_DEBUG__ + digitalWrite(__MODBUSMASTER_DEBUG_PIN_B__, true); +#endif + if (_idle) + { + _idle(); + if (_serial->available()) continue; + } +#if __MODBUSMASTER_DEBUG__ + digitalWrite(__MODBUSMASTER_DEBUG_PIN_B__, false); +#endif + } + + if ((millis() - u32StartTime) > ku16MBResponseTimeout) + { + u8MBStatus = ku8MBResponseTimedOut; + } + } + + // verify response is large enough to inspect further + if (!u8MBStatus && u8ModbusADUSize >= 4) + { + // calculate CRC + u16CRC = 0xFFFF; + for (uint8_t i = 0; i < (u8ModbusADUSize - 2); i++) + { + u16CRC = crc16_update(u16CRC, u8ModbusADU[i]); + } + + // verify CRC + if (!u8MBStatus && (lowByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 2] || + highByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 1])) + { + u8MBStatus = ku8MBInvalidCRC; + } + } + + _u8TransmitBufferIndex = 0; + u16TransmitBufferLength = 0; + _u8ResponseBufferIndex = 0; + return u8MBStatus; +} + void ModbusMaster::setResponseBuffer(uint16_t *bufPtr,size_t bufLen) + { + ku8MaxBufferSize=bufLen; + _u16ResponseBuffer=bufPtr; + + } + + void ModbusMaster::setDefaultResponseBuffer() + { + ku8MaxBufferSize=ku8DefMaxBufferSize; + _u16ResponseBuffer=_u16DefResponseBuffer; + } \ No newline at end of file diff --git a/src/ModbusMaster.h b/src/ModbusMaster.h index 8c433e6..c906ac6 100644 --- a/src/ModbusMaster.h +++ b/src/ModbusMaster.h @@ -55,7 +55,7 @@ Set to 1 to enable debugging features within class: /* _____PROJECT INCLUDES_____________________________________________________ */ // functions to calculate Modbus Application Data Unit CRC -#include "util/crc16.h" +#include "util/crc16_.h" // functions to manipulate words #include "util/word.h" @@ -216,17 +216,23 @@ class ModbusMaster uint8_t maskWriteRegister(uint16_t, uint16_t, uint16_t); uint8_t readWriteMultipleRegisters(uint16_t, uint16_t, uint16_t, uint16_t); uint8_t readWriteMultipleRegisters(uint16_t, uint16_t); - + uint8_t ModbusRawTransaction(uint8_t *u8ModbusADU,uint8_t u8ModbusADUSize, uint8_t u8BytesLeft); + void setResponseBuffer(uint16_t *bufPtr,size_t bufLen); + void setDefaultResponseBuffer(); + private: Stream* _serial; ///< reference to serial port object uint8_t _u8MBSlave; ///< Modbus slave (1..255) initialized in begin() - static const uint8_t ku8MaxBufferSize = 64; ///< size of response/transmit buffers + + static const uint8_t ku8DefMaxBufferSize = 64; ///< size of response/transmit buffers + uint8_t ku8MaxBufferSize ; uint16_t _u16ReadAddress; ///< slave register from which to read uint16_t _u16ReadQty; ///< quantity of words to read - uint16_t _u16ResponseBuffer[ku8MaxBufferSize]; ///< buffer to store Modbus slave response; read via GetResponseBuffer() + uint16_t _u16DefResponseBuffer[ku8DefMaxBufferSize]; ///< buffer to store Modbus slave response; read via GetResponseBuffer() + uint16_t *_u16ResponseBuffer; uint16_t _u16WriteAddress; ///< slave register to which to write - uint16_t _u16WriteQty; ///< quantity of words to write - uint16_t _u16TransmitBuffer[ku8MaxBufferSize]; ///< buffer containing data to transmit to Modbus slave; set via SetTransmitBuffer() + uint16_t _u16WriteQty; //< quantity of words to write + uint16_t _u16TransmitBuffer[ku8DefMaxBufferSize]; ///< buffer containing data to transmit to Modbus slave; set via SetTransmitBuffer() uint16_t* txBuffer; // from Wire.h -- need to clean this up Rx uint8_t _u8TransmitBufferIndex; uint16_t u16TransmitBufferLength; @@ -260,6 +266,8 @@ class ModbusMaster void (*_preTransmission)(); // postTransmission callback function; gets called after a Modbus message has been sent void (*_postTransmission)(); + + }; #endif diff --git a/src/util/crc16.h b/src/util/crc16_.h similarity index 100% rename from src/util/crc16.h rename to src/util/crc16_.h