Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for v2.0.1 #85

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/Basic/Basic.pde
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*/

#include <ModbusMaster.h>
#include <util/word.h>


// instantiate ModbusMaster object
Expand Down
1 change: 1 addition & 0 deletions examples/PhoenixContact_nanoLC/PhoenixContact_nanoLC.pde
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/

#include <ModbusMaster.h>
#include <util/word.h>

// discrete coils
#define NANO_DO(n) (0x0000 + n) ///< returns nanoLC discrete output address
Expand Down
1 change: 1 addition & 0 deletions examples/RS485_HalfDuplex/RS485_HalfDuplex.ino
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
*/

#include <ModbusMaster.h>
#include <util/word.h>

/*!
We're using a MAX485-compatible RS485 Transceiver.
Expand Down
158 changes: 113 additions & 45 deletions src/ModbusMaster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ Arduino library for communicating with Modbus slaves over RS232/485 (via RTU pro
/* _____PROJECT INCLUDES_____________________________________________________ */
#include "ModbusMaster.h"

// functions to calculate Modbus Application Data Unit CRC
#include "util/crc16.h"

// functions to manipulate words
#include "util/word.h"

/* _____GLOBAL VARIABLES_____________________________________________________ */

Expand All @@ -46,6 +51,8 @@ ModbusMaster::ModbusMaster(void)
_idle = 0;
_preTransmission = 0;
_postTransmission = 0;
_logTransmit = 0;
_logReceive = 0;
}

/**
Expand Down Expand Up @@ -83,7 +90,7 @@ void ModbusMaster::beginTransmission(uint16_t u16Address)
// eliminate this function in favor of using existing MB request functions
uint8_t ModbusMaster::requestFrom(uint16_t address, uint16_t quantity)
{
uint8_t read;
uint8_t read = 0;
// clamp to buffer length
if (quantity > ku8MaxBufferSize)
{
Expand Down Expand Up @@ -216,6 +223,45 @@ void ModbusMaster::postTransmission(void (*postTransmission)())
_postTransmission = postTransmission;
}

/**
Set transmit log callback function.

This function gets called before a Modbus message is sent.

Typical usage of this callback is for debugging the messages that are
sent on the bus.

@see ModbusMaster::ModbusMasterTransaction()
@see ModbusMaster::logReceive()
*/
void ModbusMaster::logTransmit(void (*logTransmit)(const uint8_t *data,
size_t length))
{
_logTransmit = logTransmit;
}

/**
Set receive log callback function.

This function gets called after a Modbus message is received.

It will only be called with the data that has been read, this may be
shorter than the data on the bus if there was an error in the header.
The status is provided to allow this to be detected or to only log
messages where there was an error.

Typical usage of this callback is for debugging the messages that are
received on the bus.

@see ModbusMaster::ModbusMasterTransaction()
@see ModbusMaster::logReceive()
*/
void ModbusMaster::logReceive(void (*logReceive)(const uint8_t *data,
size_t length, uint8_t status))
{
_logReceive = logReceive;
}


/**
Retrieve data from response buffer.
Expand Down Expand Up @@ -600,16 +646,17 @@ Modbus transaction engine.
uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction)
{
uint8_t u8ModbusADU[256];
uint8_t u8ModbusADUSize = 0;
uint8_t i, u8Qty;
uint16_t u16ModbusADUSize = 0;
uint16_t i;
uint8_t u8Qty;
uint16_t u16CRC;
uint32_t u32StartTime;
uint8_t u8BytesLeft = 8;
uint16_t u8BytesLeft = 8;
uint8_t u8MBStatus = ku8MBSuccess;

// assemble Modbus Request Application Data Unit
u8ModbusADU[u8ModbusADUSize++] = _u8MBSlave;
u8ModbusADU[u8ModbusADUSize++] = u8MBFunction;
u8ModbusADU[u16ModbusADUSize++] = _u8MBSlave;
u8ModbusADU[u16ModbusADUSize++] = u8MBFunction;

switch(u8MBFunction)
{
Expand All @@ -618,10 +665,10 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction)
case ku8MBReadInputRegisters:
case ku8MBReadHoldingRegisters:
case ku8MBReadWriteMultipleRegisters:
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16ReadAddress);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16ReadAddress);
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16ReadQty);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16ReadQty);
u8ModbusADU[u16ModbusADUSize++] = highByte(_u16ReadAddress);
u8ModbusADU[u16ModbusADUSize++] = lowByte(_u16ReadAddress);
u8ModbusADU[u16ModbusADUSize++] = highByte(_u16ReadQty);
u8ModbusADU[u16ModbusADUSize++] = lowByte(_u16ReadQty);
break;
}

Expand All @@ -633,73 +680,78 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction)
case ku8MBWriteSingleRegister:
case ku8MBWriteMultipleRegisters:
case ku8MBReadWriteMultipleRegisters:
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteAddress);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteAddress);
u8ModbusADU[u16ModbusADUSize++] = highByte(_u16WriteAddress);
u8ModbusADU[u16ModbusADUSize++] = lowByte(_u16WriteAddress);
break;
}

switch(u8MBFunction)
{
case ku8MBWriteSingleCoil:
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteQty);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty);
u8ModbusADU[u16ModbusADUSize++] = highByte(_u16WriteQty);
u8ModbusADU[u16ModbusADUSize++] = lowByte(_u16WriteQty);
break;

case ku8MBWriteSingleRegister:
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[0]);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[0]);
u8ModbusADU[u16ModbusADUSize++] = highByte(_u16TransmitBuffer[0]);
u8ModbusADU[u16ModbusADUSize++] = lowByte(_u16TransmitBuffer[0]);
break;

case ku8MBWriteMultipleCoils:
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteQty);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty);
u8ModbusADU[u16ModbusADUSize++] = highByte(_u16WriteQty);
u8ModbusADU[u16ModbusADUSize++] = lowByte(_u16WriteQty);
u8Qty = (_u16WriteQty % 8) ? ((_u16WriteQty >> 3) + 1) : (_u16WriteQty >> 3);
u8ModbusADU[u8ModbusADUSize++] = u8Qty;
u8ModbusADU[u16ModbusADUSize++] = u8Qty;
for (i = 0; i < u8Qty; i++)
{
switch(i % 2)
{
case 0: // i is even
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[i >> 1]);
u8ModbusADU[u16ModbusADUSize++] = lowByte(_u16TransmitBuffer[i >> 1]);
break;

case 1: // i is odd
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[i >> 1]);
u8ModbusADU[u16ModbusADUSize++] = highByte(_u16TransmitBuffer[i >> 1]);
break;
}
}
break;

case ku8MBWriteMultipleRegisters:
case ku8MBReadWriteMultipleRegisters:
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16WriteQty);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16WriteQty << 1);
u8ModbusADU[u16ModbusADUSize++] = highByte(_u16WriteQty);
u8ModbusADU[u16ModbusADUSize++] = lowByte(_u16WriteQty);
u8ModbusADU[u16ModbusADUSize++] = lowByte(_u16WriteQty << 1);

for (i = 0; i < lowByte(_u16WriteQty); i++)
{
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[i]);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[i]);
u8ModbusADU[u16ModbusADUSize++] = highByte(_u16TransmitBuffer[i]);
u8ModbusADU[u16ModbusADUSize++] = lowByte(_u16TransmitBuffer[i]);
}
break;

case ku8MBMaskWriteRegister:
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[0]);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[0]);
u8ModbusADU[u8ModbusADUSize++] = highByte(_u16TransmitBuffer[1]);
u8ModbusADU[u8ModbusADUSize++] = lowByte(_u16TransmitBuffer[1]);
u8ModbusADU[u16ModbusADUSize++] = highByte(_u16TransmitBuffer[0]);
u8ModbusADU[u16ModbusADUSize++] = lowByte(_u16TransmitBuffer[0]);
u8ModbusADU[u16ModbusADUSize++] = highByte(_u16TransmitBuffer[1]);
u8ModbusADU[u16ModbusADUSize++] = lowByte(_u16TransmitBuffer[1]);
break;
}

// append CRC
u16CRC = 0xFFFF;
for (i = 0; i < u8ModbusADUSize; i++)
for (i = 0; i < u16ModbusADUSize; i++)
{
u16CRC = crc16_update(u16CRC, u8ModbusADU[i]);
}
u8ModbusADU[u8ModbusADUSize++] = lowByte(u16CRC);
u8ModbusADU[u8ModbusADUSize++] = highByte(u16CRC);
u8ModbusADU[u8ModbusADUSize] = 0;
u8ModbusADU[u16ModbusADUSize++] = lowByte(u16CRC);
u8ModbusADU[u16ModbusADUSize++] = highByte(u16CRC);
u8ModbusADU[u16ModbusADUSize] = 0;

if (_logTransmit)
{
_logTransmit(u8ModbusADU, u16ModbusADUSize);
}

// flush receive buffer before transmitting request
while (_serial->read() != -1);
Expand All @@ -709,12 +761,12 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction)
{
_preTransmission();
}
for (i = 0; i < u8ModbusADUSize; i++)
for (i = 0; i < u16ModbusADUSize; i++)
{
_serial->write(u8ModbusADU[i]);
}

u8ModbusADUSize = 0;
u16ModbusADUSize = 0;
_serial->flush(); // flush transmit buffer
if (_postTransmission)
{
Expand All @@ -725,13 +777,24 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction)
u32StartTime = millis();
while (u8BytesLeft && !u8MBStatus)
{
if (u16ModbusADUSize >= sizeof(u8ModbusADU))
{
u8MBStatus = ku8MBResponseTooLarge;
break;
}

if (_serial->available())
{
#if __MODBUSMASTER_DEBUG__
digitalWrite(__MODBUSMASTER_DEBUG_PIN_A__, true);
#endif
u8ModbusADU[u8ModbusADUSize++] = _serial->read();
u8BytesLeft--;
int data = _serial->read();

if (data != -1)
{
u8ModbusADU[u16ModbusADUSize++] = data;
u8BytesLeft--;
}
#if __MODBUSMASTER_DEBUG__
digitalWrite(__MODBUSMASTER_DEBUG_PIN_A__, false);
#endif
Expand All @@ -751,7 +814,7 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction)
}

// evaluate slave ID, function code once enough bytes have been read
if (u8ModbusADUSize == 5)
if (u16ModbusADUSize == 5)
{
// verify response is for correct Modbus slave
if (u8ModbusADU[0] != _u8MBSlave)
Expand Down Expand Up @@ -804,23 +867,28 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction)
}

// verify response is large enough to inspect further
if (!u8MBStatus && u8ModbusADUSize >= 5)
if (!u8MBStatus && u16ModbusADUSize >= 5)
{
// calculate CRC
u16CRC = 0xFFFF;
for (i = 0; i < (u8ModbusADUSize - 2); i++)
for (i = 0; i < (u16ModbusADUSize - 2); i++)
{
u16CRC = crc16_update(u16CRC, u8ModbusADU[i]);
}

// verify CRC
if (!u8MBStatus && (lowByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 2] ||
highByte(u16CRC) != u8ModbusADU[u8ModbusADUSize - 1]))
if (!u8MBStatus && (lowByte(u16CRC) != u8ModbusADU[u16ModbusADUSize - 2] ||
highByte(u16CRC) != u8ModbusADU[u16ModbusADUSize - 1]))
{
u8MBStatus = ku8MBInvalidCRC;
}
}

if (_logReceive)
{
_logReceive(u8ModbusADU, u16ModbusADUSize, u8MBStatus);
}

// disassemble ADU into words
if (!u8MBStatus)
{
Expand All @@ -830,7 +898,7 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction)
case ku8MBReadCoils:
case ku8MBReadDiscreteInputs:
// load bytes into word; response bytes are ordered L, H, L, H, ...
for (i = 0; i < (u8ModbusADU[2] >> 1); i++)
for (i = 0; i < (uint8_t)(u8ModbusADU[2] >> 1); i++)
{
if (i < ku8MaxBufferSize)
{
Expand All @@ -856,7 +924,7 @@ uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction)
case ku8MBReadHoldingRegisters:
case ku8MBReadWriteMultipleRegisters:
// load bytes into word; response bytes are ordered H, L, H, L, ...
for (i = 0; i < (u8ModbusADU[2] >> 1); i++)
for (i = 0; i < (uint8_t)(u8ModbusADU[2] >> 1); i++)
{
if (i < ku8MaxBufferSize)
{
Expand Down
20 changes: 15 additions & 5 deletions src/ModbusMaster.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,6 @@ Set to 1 to enable debugging features within class:


/* _____PROJECT INCLUDES_____________________________________________________ */
// functions to calculate Modbus Application Data Unit CRC
#include "util/crc16.h"

// functions to manipulate words
#include "util/word.h"


/* _____CLASS DEFINITIONS____________________________________________________ */
Expand All @@ -75,6 +70,8 @@ class ModbusMaster
void idle(void (*)());
void preTransmission(void (*)());
void postTransmission(void (*)());
void logTransmit(void (*)(const uint8_t *data, size_t length));
void logReceive(void (*)(const uint8_t *data, size_t length, uint8_t status));

// Modbus exception codes
/**
Expand Down Expand Up @@ -188,6 +185,15 @@ class ModbusMaster
*/
static const uint8_t ku8MBInvalidCRC = 0xE3;

/**
ModbusMaster response too large exception.

The response is too large to fit in the receive buffer.

@ingroup constant
*/
static const uint8_t ku8MBResponseTooLarge = 0xE4;

uint16_t getResponseBuffer(uint8_t);
void clearResponseBuffer();
uint8_t setTransmitBuffer(uint8_t, uint16_t);
Expand Down Expand Up @@ -260,6 +266,10 @@ class ModbusMaster
void (*_preTransmission)();
// postTransmission callback function; gets called after a Modbus message has been sent
void (*_postTransmission)();
// logTransmit callback function; gets called before writing a Modbus message
void (*_logTransmit)(const uint8_t *data, size_t length);
// logReceive callback function; gets called after reading a Modbus message
void (*_logReceive)(const uint8_t *data, size_t length, uint8_t status);
};
#endif

Expand Down