modbus slave works, second adc added
This commit is contained in:
@ -7,12 +7,15 @@
|
|||||||
#include "SimpleModbusSlave.h"
|
#include "SimpleModbusSlave.h"
|
||||||
|
|
||||||
const uint8_t LED_PIN = 8;
|
const uint8_t LED_PIN = 8;
|
||||||
const uint8_t ADC_CS_PIN = 9;
|
const uint8_t ADC_1_CS_PIN = 9;
|
||||||
const uint8_t ADC_RDY_PIN = 7;
|
const uint8_t ADC_1_RDY_PIN = 7;
|
||||||
|
const uint8_t ADC_2_CS_PIN = 2;
|
||||||
|
const uint8_t ADC_2_RDY_PIN = 3;
|
||||||
const uint8_t MODBUS_TX_ENABLE_PIN = 6;
|
const uint8_t MODBUS_TX_ENABLE_PIN = 6;
|
||||||
const uint8_t MODBUS_ID = 3;
|
const uint8_t MODBUS_ID = 3;
|
||||||
|
|
||||||
ADS1210 ads1210;
|
ADS1210 ads1210_1;
|
||||||
|
ADS1210 ads1210_2;
|
||||||
LED led;
|
LED led;
|
||||||
Metro secondTick = Metro(1000);
|
Metro secondTick = Metro(1000);
|
||||||
uint32_t uptimeSeconds;
|
uint32_t uptimeSeconds;
|
||||||
@ -22,27 +25,35 @@ struct {
|
|||||||
union {
|
union {
|
||||||
uint32_t in;
|
uint32_t in;
|
||||||
uint16_t modbusRegisters[2]; // 0, 1
|
uint16_t modbusRegisters[2]; // 0, 1
|
||||||
} adcValue;
|
} adc1Value;
|
||||||
union {
|
union {
|
||||||
uint32_t in;
|
uint32_t in;
|
||||||
uint16_t modbusRegisters[2]; // 2, 3
|
uint16_t modbusRegisters[2]; // 2, 3
|
||||||
|
} adc2Value;
|
||||||
|
union {
|
||||||
|
uint32_t in;
|
||||||
|
uint16_t modbusRegisters[2]; // 4, 5
|
||||||
} uptimeSeconds;
|
} uptimeSeconds;
|
||||||
} modbusHoldingRegisters;
|
} modbusHoldingRegisters;
|
||||||
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
led.begin(LED_PIN);
|
led.begin(LED_PIN);
|
||||||
ads1210.begin(ADC_CS_PIN, ADC_RDY_PIN);
|
ads1210_1.begin(ADC_1_CS_PIN, ADC_1_RDY_PIN);
|
||||||
|
ads1210_2.begin(ADC_2_CS_PIN, ADC_2_RDY_PIN);
|
||||||
modbus_configure(&Serial, 1200, SERIAL_8N2, MODBUS_ID, MODBUS_TX_ENABLE_PIN,
|
modbus_configure(&Serial, 1200, SERIAL_8N2, MODBUS_ID, MODBUS_TX_ENABLE_PIN,
|
||||||
sizeof(modbusHoldingRegisters), modbusHoldingRegisters)
|
sizeof(modbusHoldingRegisters), (uint16_t*)(&modbusHoldingRegisters));
|
||||||
uptimeSeconds = 0;
|
uptimeSeconds = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
modbus_update();
|
modbus_update();
|
||||||
|
|
||||||
ads1210.exec();
|
ads1210_1.exec();
|
||||||
modbusHoldingRegisters.adcValue.in = ads1210.value;
|
modbusHoldingRegisters.adc1Value.in = ads1210_1.value;
|
||||||
|
|
||||||
|
ads1210_2.exec();
|
||||||
|
modbusHoldingRegisters.adc2Value.in = ads1210_2.value;
|
||||||
|
|
||||||
if (secondTick.check() == 1) {
|
if (secondTick.check() == 1) {
|
||||||
led.toggle();
|
led.toggle();
|
||||||
|
@ -1,515 +0,0 @@
|
|||||||
#include "SimpleModbusMaster.h"
|
|
||||||
#include "HardwareSerial.h"
|
|
||||||
|
|
||||||
// state machine states
|
|
||||||
#define IDLE 1
|
|
||||||
#define WAITING_FOR_REPLY 2
|
|
||||||
#define WAITING_FOR_TURNAROUND 3
|
|
||||||
|
|
||||||
#define BUFFER_SIZE 128
|
|
||||||
|
|
||||||
unsigned char state;
|
|
||||||
unsigned char retry_count;
|
|
||||||
unsigned char TxEnablePin;
|
|
||||||
|
|
||||||
// frame[] is used to receive and transmit packages.
|
|
||||||
// The maximum number of bytes in a modbus packet is 256 bytes
|
|
||||||
// This is limited to the serial buffer of 128 bytes
|
|
||||||
unsigned char frame[BUFFER_SIZE];
|
|
||||||
unsigned char buffer;
|
|
||||||
unsigned int timeout; // timeout interval
|
|
||||||
unsigned int polling; // turnaround delay interval
|
|
||||||
unsigned int T1_5; // inter character time out in microseconds
|
|
||||||
unsigned long delayStart; // init variable for turnaround and timeout delay
|
|
||||||
unsigned int total_no_of_packets;
|
|
||||||
Packet* packetArray; // packet starting address
|
|
||||||
Packet* packet; // current packet
|
|
||||||
HardwareSerial* ModbusPort;
|
|
||||||
|
|
||||||
// function definitions
|
|
||||||
void idle();
|
|
||||||
void constructPacket();
|
|
||||||
unsigned char construct_F15();
|
|
||||||
unsigned char construct_F16();
|
|
||||||
void waiting_for_reply();
|
|
||||||
void processReply();
|
|
||||||
void waiting_for_turnaround();
|
|
||||||
void process_F1_F2();
|
|
||||||
void process_F3_F4();
|
|
||||||
void process_F15_F16();
|
|
||||||
void processError();
|
|
||||||
void processSuccess();
|
|
||||||
unsigned int calculateCRC(unsigned char bufferSize);
|
|
||||||
void sendPacket(unsigned char bufferSize);
|
|
||||||
|
|
||||||
// Modbus Master State Machine
|
|
||||||
void modbus_update()
|
|
||||||
{
|
|
||||||
switch (state)
|
|
||||||
{
|
|
||||||
case IDLE:
|
|
||||||
idle();
|
|
||||||
break;
|
|
||||||
case WAITING_FOR_REPLY:
|
|
||||||
waiting_for_reply();
|
|
||||||
break;
|
|
||||||
case WAITING_FOR_TURNAROUND:
|
|
||||||
waiting_for_turnaround();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void idle()
|
|
||||||
{
|
|
||||||
static unsigned int packet_index;
|
|
||||||
|
|
||||||
unsigned int failed_connections = 0;
|
|
||||||
|
|
||||||
unsigned char current_connection;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (packet_index == total_no_of_packets) // wrap around to the beginning
|
|
||||||
packet_index = 0;
|
|
||||||
|
|
||||||
// proceed to the next packet
|
|
||||||
packet = &packetArray[packet_index];
|
|
||||||
|
|
||||||
// get the current connection status
|
|
||||||
current_connection = packet->connection;
|
|
||||||
|
|
||||||
if (!current_connection)
|
|
||||||
{
|
|
||||||
// If all the connection attributes are false return
|
|
||||||
// immediately to the main sketch
|
|
||||||
if (++failed_connections == total_no_of_packets)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
packet_index++;
|
|
||||||
|
|
||||||
// if a packet has no connection get the next one
|
|
||||||
}while (!current_connection);
|
|
||||||
|
|
||||||
constructPacket();
|
|
||||||
}
|
|
||||||
|
|
||||||
void constructPacket()
|
|
||||||
{
|
|
||||||
packet->requests++;
|
|
||||||
frame[0] = packet->id;
|
|
||||||
frame[1] = packet->function;
|
|
||||||
frame[2] = packet->address >> 8; // address Hi
|
|
||||||
frame[3] = packet->address & 0xFF; // address Lo
|
|
||||||
// For functions 1 & 2 data is the number of points
|
|
||||||
// For functions 3, 4 & 16 data is the number of registers
|
|
||||||
// For function 15 data is the number of coils
|
|
||||||
frame[4] = packet->data >> 8; // MSB
|
|
||||||
frame[5] = packet->data & 0xFF; // LSB
|
|
||||||
|
|
||||||
|
|
||||||
unsigned char frameSize;
|
|
||||||
|
|
||||||
// construct the frame according to the modbus function
|
|
||||||
if (packet->function == PRESET_MULTIPLE_REGISTERS)
|
|
||||||
frameSize = construct_F16();
|
|
||||||
else if (packet->function == FORCE_MULTIPLE_COILS)
|
|
||||||
frameSize = construct_F15();
|
|
||||||
else // else functions 1,2,3 & 4 is assumed. They all share the exact same request format.
|
|
||||||
frameSize = 8; // the request is always 8 bytes in size for the above mentioned functions.
|
|
||||||
|
|
||||||
unsigned int crc16 = calculateCRC(frameSize - 2);
|
|
||||||
frame[frameSize - 2] = crc16 >> 8; // split crc into 2 bytes
|
|
||||||
frame[frameSize - 1] = crc16 & 0xFF;
|
|
||||||
sendPacket(frameSize);
|
|
||||||
|
|
||||||
state = WAITING_FOR_REPLY; // state change
|
|
||||||
|
|
||||||
// if broadcast is requested (id == 0) for function 15 or 16 then override
|
|
||||||
// the previous state and force a success since the slave wont respond
|
|
||||||
if (packet->id == 0)
|
|
||||||
processSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char construct_F15()
|
|
||||||
{
|
|
||||||
// function 15 coil information is packed LSB first until the first 16 bits are completed
|
|
||||||
// It is received the same way..
|
|
||||||
unsigned char no_of_registers = packet->data / 16;
|
|
||||||
unsigned char no_of_bytes = no_of_registers * 2;
|
|
||||||
|
|
||||||
// if the number of points dont fit in even 2byte amounts (one register) then use another register and pad
|
|
||||||
if (packet->data % 16 > 0)
|
|
||||||
{
|
|
||||||
no_of_registers++;
|
|
||||||
no_of_bytes++;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame[6] = no_of_bytes;
|
|
||||||
unsigned char bytes_processed = 0;
|
|
||||||
unsigned char index = 7; // user data starts at index 7
|
|
||||||
unsigned int temp;
|
|
||||||
|
|
||||||
for (unsigned char i = 0; i < no_of_registers; i++)
|
|
||||||
{
|
|
||||||
temp = packet->register_array[i]; // get the data
|
|
||||||
frame[index] = temp & 0xFF;
|
|
||||||
bytes_processed++;
|
|
||||||
|
|
||||||
if (bytes_processed < no_of_bytes)
|
|
||||||
{
|
|
||||||
frame[index + 1] = temp >> 8;
|
|
||||||
bytes_processed++;
|
|
||||||
index += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unsigned char frameSize = (9 + no_of_bytes); // first 7 bytes of the array + 2 bytes CRC + noOfBytes
|
|
||||||
return frameSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char construct_F16()
|
|
||||||
{
|
|
||||||
unsigned char no_of_bytes = packet->data * 2;
|
|
||||||
|
|
||||||
// first 6 bytes of the array + no_of_bytes + 2 bytes CRC
|
|
||||||
frame[6] = no_of_bytes; // number of bytes
|
|
||||||
unsigned char index = 7; // user data starts at index 7
|
|
||||||
unsigned char no_of_registers = packet->data;
|
|
||||||
unsigned int temp;
|
|
||||||
|
|
||||||
for (unsigned char i = 0; i < no_of_registers; i++)
|
|
||||||
{
|
|
||||||
temp = packet->register_array[i]; // get the data
|
|
||||||
frame[index] = temp >> 8;
|
|
||||||
index++;
|
|
||||||
frame[index] = temp & 0xFF;
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
unsigned char frameSize = (9 + no_of_bytes); // first 7 bytes of the array + 2 bytes CRC + noOfBytes
|
|
||||||
return frameSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void waiting_for_turnaround()
|
|
||||||
{
|
|
||||||
if ((millis() - delayStart) > polling)
|
|
||||||
state = IDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the serial data from the buffer
|
|
||||||
void waiting_for_reply()
|
|
||||||
{
|
|
||||||
if ((*ModbusPort).available()) // is there something to check?
|
|
||||||
{
|
|
||||||
unsigned char overflowFlag = 0;
|
|
||||||
buffer = 0;
|
|
||||||
while ((*ModbusPort).available())
|
|
||||||
{
|
|
||||||
// The maximum number of bytes is limited to the serial buffer size
|
|
||||||
// of BUFFER_SIZE. If more bytes is received than the BUFFER_SIZE the
|
|
||||||
// overflow flag will be set and the serial buffer will be read until
|
|
||||||
// all the data is cleared from the receive buffer, while the slave is
|
|
||||||
// still responding.
|
|
||||||
if (overflowFlag)
|
|
||||||
(*ModbusPort).read();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (buffer == BUFFER_SIZE)
|
|
||||||
overflowFlag = 1;
|
|
||||||
|
|
||||||
frame[buffer] = (*ModbusPort).read();
|
|
||||||
// Serial.print("R: "); Serial.println(frame[buffer], 16);
|
|
||||||
buffer++;
|
|
||||||
}
|
|
||||||
// This is not 100% correct but it will suffice.
|
|
||||||
// worst case scenario is if more than one character time expires
|
|
||||||
// while reading from the buffer then the buffer is most likely empty
|
|
||||||
// If there are more bytes after such a delay it is not supposed to
|
|
||||||
// be received and thus will force a frame_error.
|
|
||||||
delayMicroseconds(T1_5); // inter character time out
|
|
||||||
}
|
|
||||||
|
|
||||||
// The minimum buffer size from a slave can be an exception response of
|
|
||||||
// 5 bytes. If the buffer was partially filled set a frame_error.
|
|
||||||
// The maximum number of bytes in a modbus packet is 256 bytes.
|
|
||||||
// The serial buffer limits this to 128 bytes.
|
|
||||||
|
|
||||||
if ((buffer < 5) || overflowFlag)
|
|
||||||
processError();
|
|
||||||
|
|
||||||
// Modbus over serial line datasheet states that if an unexpected slave
|
|
||||||
// responded the master must do nothing and continue with the time out.
|
|
||||||
// This seems silly cause if an incorrect slave responded you would want to
|
|
||||||
// have a quick turnaround and poll the right one again. If an unexpected
|
|
||||||
// slave responded it will most likely be a frame error in any event
|
|
||||||
else if (frame[0] != packet->id) // check id returned
|
|
||||||
processError();
|
|
||||||
else
|
|
||||||
processReply();
|
|
||||||
}
|
|
||||||
else if ((millis() - delayStart) > timeout) // check timeout
|
|
||||||
{
|
|
||||||
processError();
|
|
||||||
state = IDLE; //state change, override processError() state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void processReply()
|
|
||||||
{
|
|
||||||
// combine the crc Low & High bytes
|
|
||||||
unsigned int received_crc = ((frame[buffer - 2] << 8) | frame[buffer - 1]);
|
|
||||||
unsigned int calculated_crc = calculateCRC(buffer - 2);
|
|
||||||
|
|
||||||
if (calculated_crc == received_crc) // verify checksum
|
|
||||||
{
|
|
||||||
// To indicate an exception response a slave will 'OR'
|
|
||||||
// the requested function with 0x80
|
|
||||||
if ((frame[1] & 0x80) == 0x80) // extract 0x80
|
|
||||||
{
|
|
||||||
packet->exception_errors++;
|
|
||||||
processError();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (frame[1]) // check function returned
|
|
||||||
{
|
|
||||||
case READ_COIL_STATUS:
|
|
||||||
case READ_INPUT_STATUS:
|
|
||||||
process_F1_F2();
|
|
||||||
break;
|
|
||||||
case READ_INPUT_REGISTERS:
|
|
||||||
case READ_HOLDING_REGISTERS:
|
|
||||||
process_F3_F4();
|
|
||||||
break;
|
|
||||||
case FORCE_MULTIPLE_COILS:
|
|
||||||
case PRESET_MULTIPLE_REGISTERS:
|
|
||||||
process_F15_F16();
|
|
||||||
break;
|
|
||||||
default: // illegal function returned
|
|
||||||
processError();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // checksum failed
|
|
||||||
{
|
|
||||||
processError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void process_F1_F2()
|
|
||||||
{
|
|
||||||
// packet->data for function 1 & 2 is actually the number of boolean points
|
|
||||||
unsigned char no_of_registers = packet->data / 16;
|
|
||||||
unsigned char number_of_bytes = no_of_registers * 2;
|
|
||||||
|
|
||||||
// if the number of points dont fit in even 2byte amounts (one register) then use another register and pad
|
|
||||||
if (packet->data % 16 > 0)
|
|
||||||
{
|
|
||||||
no_of_registers++;
|
|
||||||
number_of_bytes++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frame[2] == number_of_bytes) // check number of bytes returned
|
|
||||||
{
|
|
||||||
unsigned char bytes_processed = 0;
|
|
||||||
unsigned char index = 3; // start at the 4th element in the frame and combine the Lo byte
|
|
||||||
unsigned int temp;
|
|
||||||
for (unsigned char i = 0; i < no_of_registers; i++)
|
|
||||||
{
|
|
||||||
temp = frame[index];
|
|
||||||
bytes_processed++;
|
|
||||||
if (bytes_processed < number_of_bytes)
|
|
||||||
{
|
|
||||||
temp = (frame[index + 1] << 8) | temp;
|
|
||||||
bytes_processed++;
|
|
||||||
index += 2;
|
|
||||||
}
|
|
||||||
packet->register_array[i] = temp;
|
|
||||||
}
|
|
||||||
processSuccess();
|
|
||||||
}
|
|
||||||
else // incorrect number of bytes returned
|
|
||||||
processError();
|
|
||||||
}
|
|
||||||
|
|
||||||
void process_F3_F4()
|
|
||||||
{
|
|
||||||
// check number of bytes returned - unsigned int == 2 bytes
|
|
||||||
// data for function 3 & 4 is the number of registers
|
|
||||||
if (frame[2] == (packet->data * 2))
|
|
||||||
{
|
|
||||||
unsigned char index = 3;
|
|
||||||
for (unsigned char i = 0; i < packet->data; i++)
|
|
||||||
{
|
|
||||||
// start at the 4th element in the frame and combine the Lo byte
|
|
||||||
packet->register_array[i] = (frame[index] << 8) | frame[index + 1];
|
|
||||||
index += 2;
|
|
||||||
}
|
|
||||||
processSuccess();
|
|
||||||
}
|
|
||||||
else // incorrect number of bytes returned
|
|
||||||
processError();
|
|
||||||
}
|
|
||||||
|
|
||||||
void process_F15_F16()
|
|
||||||
{
|
|
||||||
// Functions 15 & 16 is just an echo of the query
|
|
||||||
unsigned int recieved_address = ((frame[2] << 8) | frame[3]);
|
|
||||||
unsigned int recieved_data = ((frame[4] << 8) | frame[5]);
|
|
||||||
|
|
||||||
if ((recieved_address == packet->address) && (recieved_data == packet->data))
|
|
||||||
processSuccess();
|
|
||||||
else
|
|
||||||
processError();
|
|
||||||
}
|
|
||||||
|
|
||||||
void processError()
|
|
||||||
{
|
|
||||||
packet->retries++;
|
|
||||||
packet->failed_requests++;
|
|
||||||
|
|
||||||
if (packet->valueValid != NULL) {
|
|
||||||
*(packet->valueValid) = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the number of retries have reached the max number of retries
|
|
||||||
// allowable, stop requesting the specific packet
|
|
||||||
if (packet->retries == retry_count)
|
|
||||||
{
|
|
||||||
packet->connection = 0;
|
|
||||||
packet->retries = 0;
|
|
||||||
|
|
||||||
if (packet->connectionValid != NULL) {
|
|
||||||
*(packet->connectionValid) = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
state = WAITING_FOR_TURNAROUND;
|
|
||||||
delayStart = millis(); // start the turnaround delay
|
|
||||||
}
|
|
||||||
|
|
||||||
void processSuccess()
|
|
||||||
{
|
|
||||||
packet->successful_requests++; // transaction sent successfully
|
|
||||||
packet->retries = 0; // if a request was successful reset the retry counter
|
|
||||||
state = WAITING_FOR_TURNAROUND;
|
|
||||||
delayStart = millis(); // start the turnaround delay
|
|
||||||
|
|
||||||
if (packet->valueValid != NULL) {
|
|
||||||
*(packet->valueValid) = true;
|
|
||||||
}
|
|
||||||
if (packet->connectionValid != NULL) {
|
|
||||||
*(packet->connectionValid) = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void modbus_configure(HardwareSerial* SerialPort,
|
|
||||||
long baud,
|
|
||||||
unsigned char byteFormat,
|
|
||||||
unsigned int _timeout,
|
|
||||||
unsigned int _polling,
|
|
||||||
unsigned char _retry_count,
|
|
||||||
unsigned char _TxEnablePin,
|
|
||||||
Packet* _packets,
|
|
||||||
unsigned int _total_no_of_packets)
|
|
||||||
{
|
|
||||||
// Modbus states that a baud rate higher than 19200 must use a fixed 750 us
|
|
||||||
// for inter character time out and 1.75 ms for a frame delay for baud rates
|
|
||||||
// below 19200 the timing is more critical and has to be calculated.
|
|
||||||
// E.g. 9600 baud in a 11 bit packet is 9600/11 = 872 characters per second
|
|
||||||
// In milliseconds this will be 872 characters per 1000ms. So for 1 character
|
|
||||||
// 1000ms/872 characters is 1.14583ms per character and finally modbus states
|
|
||||||
// an inter-character must be 1.5T or 1.5 times longer than a character. Thus
|
|
||||||
// 1.5T = 1.14583ms * 1.5 = 1.71875ms. A frame delay is 3.5T.
|
|
||||||
// Thus the formula is T1.5(us) = (1000ms * 1000(us) * 1.5 * 11bits)/baud
|
|
||||||
// 1000ms * 1000(us) * 1.5 * 11bits = 16500000 can be calculated as a constant
|
|
||||||
|
|
||||||
if (baud > 19200)
|
|
||||||
T1_5 = 750;
|
|
||||||
else
|
|
||||||
T1_5 = 16500000/baud; // 1T * 1.5 = T1.5
|
|
||||||
|
|
||||||
// initialize
|
|
||||||
state = IDLE;
|
|
||||||
timeout = _timeout;
|
|
||||||
polling = _polling;
|
|
||||||
retry_count = _retry_count;
|
|
||||||
TxEnablePin = _TxEnablePin;
|
|
||||||
total_no_of_packets = _total_no_of_packets;
|
|
||||||
packetArray = _packets;
|
|
||||||
|
|
||||||
// initialize connection status of each packet
|
|
||||||
/*for (unsigned char i = 0; i < total_no_of_packets; i++)
|
|
||||||
{
|
|
||||||
_packets->connection = 1;
|
|
||||||
_packets++;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
ModbusPort = SerialPort;
|
|
||||||
(*ModbusPort).begin(baud, byteFormat);
|
|
||||||
|
|
||||||
pinMode(TxEnablePin, OUTPUT);
|
|
||||||
digitalWrite(TxEnablePin, LOW);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void modbus_construct(Packet *_packet,
|
|
||||||
unsigned char id,
|
|
||||||
unsigned char function,
|
|
||||||
unsigned int address,
|
|
||||||
unsigned int data,
|
|
||||||
unsigned int* register_array)
|
|
||||||
{
|
|
||||||
_packet->id = id;
|
|
||||||
_packet->function = function;
|
|
||||||
_packet->address = address;
|
|
||||||
_packet->data = data;
|
|
||||||
_packet->register_array = register_array;
|
|
||||||
_packet->connection = 1;
|
|
||||||
|
|
||||||
_packet->connectionValid = NULL;
|
|
||||||
_packet->valueValid = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int calculateCRC(unsigned char bufferSize)
|
|
||||||
{
|
|
||||||
unsigned int temp, temp2, flag;
|
|
||||||
temp = 0xFFFF;
|
|
||||||
for (unsigned char i = 0; i < bufferSize; i++)
|
|
||||||
{
|
|
||||||
temp = temp ^ frame[i];
|
|
||||||
for (unsigned char j = 1; j <= 8; j++)
|
|
||||||
{
|
|
||||||
flag = temp & 0x0001;
|
|
||||||
temp >>= 1;
|
|
||||||
if (flag)
|
|
||||||
temp ^= 0xA001;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Reverse byte order.
|
|
||||||
temp2 = temp >> 8;
|
|
||||||
temp = (temp << 8) | temp2;
|
|
||||||
temp &= 0xFFFF;
|
|
||||||
// the returned value is already swapped
|
|
||||||
// crcLo byte is first & crcHi byte is last
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendPacket(unsigned char bufferSize)
|
|
||||||
{
|
|
||||||
digitalWrite(TxEnablePin, HIGH);
|
|
||||||
|
|
||||||
for (unsigned char i = 0; i < bufferSize; i++) {
|
|
||||||
// Serial.print("S: "); Serial.println(frame[i],16);
|
|
||||||
(*ModbusPort).write(frame[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
(*ModbusPort).flush();
|
|
||||||
|
|
||||||
// It may be necessary to add a another character delay T1_5 here to
|
|
||||||
// avoid truncating the message on slow and long distance connections
|
|
||||||
|
|
||||||
digitalWrite(TxEnablePin, LOW);
|
|
||||||
|
|
||||||
delayStart = millis(); // start the timeout delay
|
|
||||||
}
|
|
@ -1,139 +0,0 @@
|
|||||||
#ifndef SIMPLE_MODBUS_MASTER_H
|
|
||||||
#define SIMPLE_MODBUS_MASTER_H
|
|
||||||
|
|
||||||
// SimpleModbusMasterV10
|
|
||||||
|
|
||||||
/*
|
|
||||||
SimpleModbusMaster allows you to communicate
|
|
||||||
to any slave using the Modbus RTU protocol.
|
|
||||||
|
|
||||||
To communicate with a slave you need to create a packet that will contain
|
|
||||||
all the information required to communicate to the slave.
|
|
||||||
Information counters are implemented for further diagnostic.
|
|
||||||
These are variables already implemented in a packet.
|
|
||||||
You can set and clear these variables as needed.
|
|
||||||
|
|
||||||
The following modbus information counters are implemented:
|
|
||||||
|
|
||||||
requests - contains the total requests to a slave
|
|
||||||
successful_requests - contains the total successful requests
|
|
||||||
failed_requests - general frame errors, checksum failures and buffer failures
|
|
||||||
retries - contains the number of retries
|
|
||||||
exception_errors - contains the specific modbus exception response count
|
|
||||||
These are normally illegal function, illegal address, illegal data value
|
|
||||||
or a miscellaneous error response.
|
|
||||||
|
|
||||||
And finally there is a variable called "connection" that
|
|
||||||
at any given moment contains the current connection
|
|
||||||
status of the packet. If true then the connection is
|
|
||||||
active. If false then communication will be stopped
|
|
||||||
on this packet until the programmer sets the connection
|
|
||||||
variable to true explicitly. The reason for this is
|
|
||||||
because of the time out involved in modbus communication.
|
|
||||||
Each faulty slave that's not communicating will slow down
|
|
||||||
communication on the line with the time out value. E.g.
|
|
||||||
Using a time out of 1500ms, if you have 10 slaves and 9 of them
|
|
||||||
stops communicating the latency burden placed on communication
|
|
||||||
will be 1500ms * 9 = 13,5 seconds!
|
|
||||||
Communication will automatically be stopped after the retry count expires
|
|
||||||
on each specific packet.
|
|
||||||
|
|
||||||
All the error checking, updating and communication multitasking
|
|
||||||
takes place in the background.
|
|
||||||
|
|
||||||
In general to communicate with to a slave using modbus
|
|
||||||
RTU you will request information using the specific
|
|
||||||
slave id, the function request, the starting address
|
|
||||||
and lastly the data to request.
|
|
||||||
Function 1, 2, 3, 4, 15 & 16 are supported. In addition to
|
|
||||||
this broadcasting (id = 0) is supported for function 15 & 16.
|
|
||||||
|
|
||||||
Constants are provided for:
|
|
||||||
Function 1 - READ_COIL_STATUS
|
|
||||||
Function 2 - READ_INPUT_STATUS
|
|
||||||
Function 3 - READ_HOLDING_REGISTERS
|
|
||||||
Function 4 - READ_INPUT_REGISTERS
|
|
||||||
Function 15 - FORCE_MULTIPLE_COILS
|
|
||||||
Function 16 - PRESET_MULTIPLE_REGISTERS
|
|
||||||
|
|
||||||
Note:
|
|
||||||
The Arduino serial ring buffer is 128 bytes or 64 registers.
|
|
||||||
Most of the time you will connect the Arduino using a MAX485 or similar.
|
|
||||||
|
|
||||||
In a function 3 or 4 request the master will attempt to read from a
|
|
||||||
slave and since 5 bytes is already used for ID, FUNCTION, NO OF BYTES
|
|
||||||
and two BYTES CRC the master can only request 122 bytes or 61 registers.
|
|
||||||
|
|
||||||
In a function 16 request the master will attempt to write to a
|
|
||||||
slave and since 9 bytes is already used for ID, FUNCTION, ADDRESS,
|
|
||||||
NO OF REGISTERS, NO OF BYTES and two BYTES CRC the master can only write
|
|
||||||
118 bytes or 59 registers.
|
|
||||||
|
|
||||||
Note:
|
|
||||||
Using a USB to Serial converter the maximum bytes you can send is
|
|
||||||
limited to its internal buffer which differs between manufactures.
|
|
||||||
|
|
||||||
Since it is assumed that you will mostly use the Arduino to connect without
|
|
||||||
using a USB to Serial converter the internal buffer is set the same as the
|
|
||||||
Arduino Serial ring buffer which is 128 bytes.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Arduino.h"
|
|
||||||
|
|
||||||
#define READ_COIL_STATUS 1 // Reads the ON/OFF status of discrete outputs (0X references, coils) in the slave.
|
|
||||||
#define READ_INPUT_STATUS 2 // Reads the ON/OFF status of discrete inputs (1X references) in the slave.
|
|
||||||
#define READ_HOLDING_REGISTERS 3 // Reads the binary contents of holding registers (4X references) in the slave.
|
|
||||||
#define READ_INPUT_REGISTERS 4 // Reads the binary contents of input registers (3X references) in the slave. Not writable.
|
|
||||||
#define FORCE_MULTIPLE_COILS 15 // Forces each coil (0X reference) in a sequence of coils to either ON or OFF.
|
|
||||||
#define PRESET_MULTIPLE_REGISTERS 16 // Presets values into a sequence of holding registers (4X references).
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
// specific packet info
|
|
||||||
unsigned char id;
|
|
||||||
unsigned char function;
|
|
||||||
unsigned int address;
|
|
||||||
// For functions 1 & 2 data is the number of points
|
|
||||||
// For functions 3, 4 & 16 data is the number of registers
|
|
||||||
// For function 15 data is the number of coils
|
|
||||||
unsigned int data;
|
|
||||||
unsigned int* register_array;
|
|
||||||
|
|
||||||
// modbus information counters
|
|
||||||
unsigned int requests;
|
|
||||||
unsigned int successful_requests;
|
|
||||||
unsigned int failed_requests;
|
|
||||||
unsigned int exception_errors;
|
|
||||||
unsigned int retries;
|
|
||||||
|
|
||||||
// connection status of packet
|
|
||||||
unsigned char connection;
|
|
||||||
|
|
||||||
bool *connectionValid;
|
|
||||||
bool *valueValid;
|
|
||||||
|
|
||||||
}Packet;
|
|
||||||
|
|
||||||
typedef Packet* packetPointer;
|
|
||||||
|
|
||||||
// function definitions
|
|
||||||
void modbus_update();
|
|
||||||
|
|
||||||
void modbus_construct(Packet *_packet,
|
|
||||||
unsigned char id,
|
|
||||||
unsigned char function,
|
|
||||||
unsigned int address,
|
|
||||||
unsigned int data,
|
|
||||||
unsigned int* register_array);
|
|
||||||
|
|
||||||
void modbus_configure(HardwareSerial* SerialPort,
|
|
||||||
long baud,
|
|
||||||
unsigned char byteFormat,
|
|
||||||
unsigned int _timeout,
|
|
||||||
unsigned int _polling,
|
|
||||||
unsigned char _retry_count,
|
|
||||||
unsigned char _TxEnablePin,
|
|
||||||
Packet* _packets,
|
|
||||||
unsigned int _total_no_of_packets);
|
|
||||||
|
|
||||||
#endif
|
|
262
SimpleModbusSlave.cpp
Normal file
262
SimpleModbusSlave.cpp
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
#include "SimpleModbusSlave.h"
|
||||||
|
#include "HardwareSerial.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define BUFFER_SIZE 128
|
||||||
|
|
||||||
|
// frame[] is used to recieve and transmit packages.
|
||||||
|
// The maximum serial ring buffer size is 128
|
||||||
|
unsigned char frame[BUFFER_SIZE];
|
||||||
|
unsigned int holdingRegsSize; // size of the register array
|
||||||
|
unsigned int* regs; // user array address
|
||||||
|
unsigned char broadcastFlag;
|
||||||
|
unsigned char slaveID;
|
||||||
|
unsigned char function;
|
||||||
|
unsigned char TxEnablePin;
|
||||||
|
unsigned int errorCount;
|
||||||
|
unsigned int T1_5; // inter character time out
|
||||||
|
unsigned int T3_5; // frame delay
|
||||||
|
HardwareSerial* ModbusPort;
|
||||||
|
|
||||||
|
// function definitions
|
||||||
|
void exceptionResponse(unsigned char exception);
|
||||||
|
unsigned int calculateCRC(unsigned char bufferSize);
|
||||||
|
void sendPacket(unsigned char bufferSize);
|
||||||
|
|
||||||
|
void modbus_configure(HardwareSerial *SerialPort,
|
||||||
|
long baud,
|
||||||
|
unsigned char byteFormat,
|
||||||
|
unsigned char _slaveID,
|
||||||
|
unsigned char _TxEnablePin,
|
||||||
|
unsigned int _holdingRegsSize,
|
||||||
|
unsigned int* _regs)
|
||||||
|
{
|
||||||
|
ModbusPort = SerialPort;
|
||||||
|
(*ModbusPort).begin(baud, byteFormat);
|
||||||
|
slaveID = _slaveID;
|
||||||
|
holdingRegsSize = _holdingRegsSize;
|
||||||
|
regs = _regs;
|
||||||
|
TxEnablePin = _TxEnablePin;
|
||||||
|
pinMode(TxEnablePin, OUTPUT);
|
||||||
|
digitalWrite(TxEnablePin, LOW);
|
||||||
|
errorCount = 0; // initialize errorCount
|
||||||
|
|
||||||
|
// Modbus states that a baud rate higher than 19200 must use a fixed 750 us
|
||||||
|
// for inter character time out and 1.75 ms for a frame delay for baud rates
|
||||||
|
// below 19200 the timing is more critical and has to be calculated.
|
||||||
|
// E.g. 9600 baud in a 10 bit packet is 960 characters per second
|
||||||
|
// In milliseconds this will be 960characters per 1000ms. So for 1 character
|
||||||
|
// 1000ms/960characters is 1.04167ms per character and finally modbus states
|
||||||
|
// an inter-character must be 1.5T or 1.5 times longer than a character. Thus
|
||||||
|
// 1.5T = 1.04167ms * 1.5 = 1.5625ms. A frame delay is 3.5T.
|
||||||
|
|
||||||
|
if (baud > 19200)
|
||||||
|
{
|
||||||
|
T1_5 = 750;
|
||||||
|
T3_5 = 1750;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
T1_5 = 15000000/baud; // 1T * 1.5 = T1.5
|
||||||
|
T3_5 = 35000000/baud; // 1T * 3.5 = T3.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int modbus_update()
|
||||||
|
{
|
||||||
|
if ((*ModbusPort).available())
|
||||||
|
{
|
||||||
|
unsigned char buffer = 0;
|
||||||
|
unsigned char overflow = 0;
|
||||||
|
|
||||||
|
while ((*ModbusPort).available())
|
||||||
|
{
|
||||||
|
// The maximum number of bytes is limited to the serial buffer size of 128 bytes
|
||||||
|
// If more bytes is received than the BUFFER_SIZE the overflow flag will be set and the
|
||||||
|
// serial buffer will be red untill all the data is cleared from the receive buffer.
|
||||||
|
if (overflow)
|
||||||
|
(*ModbusPort).read();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (buffer == BUFFER_SIZE)
|
||||||
|
overflow = 1;
|
||||||
|
frame[buffer] = (*ModbusPort).read();
|
||||||
|
buffer++;
|
||||||
|
}
|
||||||
|
delayMicroseconds(T1_5); // inter character time out
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an overflow occurred increment the errorCount
|
||||||
|
// variable and return to the main sketch without
|
||||||
|
// responding to the request i.e. force a timeout
|
||||||
|
if (overflow)
|
||||||
|
return errorCount++;
|
||||||
|
|
||||||
|
// The minimum request packet is 8 bytes for function 3 & 16
|
||||||
|
if (buffer > 7)
|
||||||
|
{
|
||||||
|
unsigned char id = frame[0];
|
||||||
|
|
||||||
|
broadcastFlag = 0;
|
||||||
|
|
||||||
|
if (id == 0)
|
||||||
|
broadcastFlag = 1;
|
||||||
|
|
||||||
|
if (id == slaveID || broadcastFlag) // if the recieved ID matches the slaveID or broadcasting id (0), continue
|
||||||
|
{
|
||||||
|
unsigned int crc = ((frame[buffer - 2] << 8) | frame[buffer - 1]); // combine the crc Low & High bytes
|
||||||
|
if (calculateCRC(buffer - 2) == crc) // if the calculated crc matches the recieved crc continue
|
||||||
|
{
|
||||||
|
function = frame[1];
|
||||||
|
unsigned int startingAddress = ((frame[2] << 8) | frame[3]); // combine the starting address bytes
|
||||||
|
unsigned int no_of_registers = ((frame[4] << 8) | frame[5]); // combine the number of register bytes
|
||||||
|
unsigned int maxData = startingAddress + no_of_registers;
|
||||||
|
unsigned char index;
|
||||||
|
unsigned char address;
|
||||||
|
unsigned int crc16;
|
||||||
|
|
||||||
|
// broadcasting is not supported for function 3
|
||||||
|
if (!broadcastFlag && (function == 3))
|
||||||
|
{
|
||||||
|
if (startingAddress < holdingRegsSize) // check exception 2 ILLEGAL DATA ADDRESS
|
||||||
|
{
|
||||||
|
if (maxData <= holdingRegsSize) // check exception 3 ILLEGAL DATA VALUE
|
||||||
|
{
|
||||||
|
unsigned char noOfBytes = no_of_registers * 2;
|
||||||
|
// ID, function, noOfBytes, (dataLo + dataHi)*number of registers,
|
||||||
|
// crcLo, crcHi
|
||||||
|
unsigned char responseFrameSize = 5 + noOfBytes;
|
||||||
|
frame[0] = slaveID;
|
||||||
|
frame[1] = function;
|
||||||
|
frame[2] = noOfBytes;
|
||||||
|
address = 3; // PDU starts at the 4th byte
|
||||||
|
unsigned int temp;
|
||||||
|
|
||||||
|
for (index = startingAddress; index < maxData; index++)
|
||||||
|
{
|
||||||
|
temp = regs[index];
|
||||||
|
frame[address] = temp >> 8; // split the register into 2 bytes
|
||||||
|
address++;
|
||||||
|
frame[address] = temp & 0xFF;
|
||||||
|
address++;
|
||||||
|
}
|
||||||
|
|
||||||
|
crc16 = calculateCRC(responseFrameSize - 2);
|
||||||
|
frame[responseFrameSize - 2] = crc16 >> 8; // split crc into 2 bytes
|
||||||
|
frame[responseFrameSize - 1] = crc16 & 0xFF;
|
||||||
|
sendPacket(responseFrameSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
exceptionResponse(3); // exception 3 ILLEGAL DATA VALUE
|
||||||
|
}
|
||||||
|
else
|
||||||
|
exceptionResponse(2); // exception 2 ILLEGAL DATA ADDRESS
|
||||||
|
}
|
||||||
|
else if (function == 16)
|
||||||
|
{
|
||||||
|
// Check if the recieved number of bytes matches the calculated bytes
|
||||||
|
// minus the request bytes.
|
||||||
|
// id + function + (2 * address bytes) + (2 * no of register bytes) +
|
||||||
|
// byte count + (2 * CRC bytes) = 9 bytes
|
||||||
|
if (frame[6] == (buffer - 9))
|
||||||
|
{
|
||||||
|
if (startingAddress < holdingRegsSize) // check exception 2 ILLEGAL DATA ADDRESS
|
||||||
|
{
|
||||||
|
if (maxData <= holdingRegsSize) // check exception 3 ILLEGAL DATA VALUE
|
||||||
|
{
|
||||||
|
address = 7; // start at the 8th byte in the frame
|
||||||
|
|
||||||
|
for (index = startingAddress; index < maxData; index++)
|
||||||
|
{
|
||||||
|
regs[index] = ((frame[address] << 8) | frame[address + 1]);
|
||||||
|
address += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only the first 6 bytes are used for CRC calculation
|
||||||
|
crc16 = calculateCRC(6);
|
||||||
|
frame[6] = crc16 >> 8; // split crc into 2 bytes
|
||||||
|
frame[7] = crc16 & 0xFF;
|
||||||
|
|
||||||
|
// a function 16 response is an echo of the first 6 bytes from
|
||||||
|
// the request + 2 crc bytes
|
||||||
|
if (!broadcastFlag) // don't respond if it's a broadcast message
|
||||||
|
sendPacket(8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
exceptionResponse(3); // exception 3 ILLEGAL DATA VALUE
|
||||||
|
}
|
||||||
|
else
|
||||||
|
exceptionResponse(2); // exception 2 ILLEGAL DATA ADDRESS
|
||||||
|
}
|
||||||
|
else
|
||||||
|
errorCount++; // corrupted packet
|
||||||
|
}
|
||||||
|
else
|
||||||
|
exceptionResponse(1); // exception 1 ILLEGAL FUNCTION
|
||||||
|
}
|
||||||
|
else // checksum failed
|
||||||
|
errorCount++;
|
||||||
|
} // incorrect id
|
||||||
|
}
|
||||||
|
else if (buffer > 0 && buffer < 8)
|
||||||
|
errorCount++; // corrupted packet
|
||||||
|
}
|
||||||
|
return errorCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void exceptionResponse(unsigned char exception)
|
||||||
|
{
|
||||||
|
// each call to exceptionResponse() will increment the errorCount
|
||||||
|
errorCount++;
|
||||||
|
if (!broadcastFlag) // don't respond if its a broadcast message
|
||||||
|
{
|
||||||
|
frame[0] = slaveID;
|
||||||
|
frame[1] = (function | 0x80); // set MSB bit high, informs the master of an exception
|
||||||
|
frame[2] = exception;
|
||||||
|
unsigned int crc16 = calculateCRC(3); // ID, function|0x80, exception code
|
||||||
|
frame[3] = crc16 >> 8;
|
||||||
|
frame[4] = crc16 & 0xFF;
|
||||||
|
// exception response is always 5 bytes
|
||||||
|
// ID, function + 0x80, exception code, 2 bytes crc
|
||||||
|
sendPacket(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int calculateCRC(unsigned char bufferSize)
|
||||||
|
{
|
||||||
|
unsigned int temp, temp2, flag;
|
||||||
|
temp = 0xFFFF;
|
||||||
|
for (unsigned char i = 0; i < bufferSize; i++)
|
||||||
|
{
|
||||||
|
temp = temp ^ frame[i];
|
||||||
|
for (unsigned char j = 1; j <= 8; j++)
|
||||||
|
{
|
||||||
|
flag = temp & 0x0001;
|
||||||
|
temp >>= 1;
|
||||||
|
if (flag)
|
||||||
|
temp ^= 0xA001;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Reverse byte order.
|
||||||
|
temp2 = temp >> 8;
|
||||||
|
temp = (temp << 8) | temp2;
|
||||||
|
temp &= 0xFFFF;
|
||||||
|
// the returned value is already swapped
|
||||||
|
// crcLo byte is first & crcHi byte is last
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendPacket(unsigned char bufferSize)
|
||||||
|
{
|
||||||
|
digitalWrite(TxEnablePin, HIGH);
|
||||||
|
|
||||||
|
for (unsigned char i = 0; i < bufferSize; i++)
|
||||||
|
(*ModbusPort).write(frame[i]);
|
||||||
|
|
||||||
|
(*ModbusPort).flush();
|
||||||
|
|
||||||
|
// allow a frame delay to indicate end of transmission
|
||||||
|
delayMicroseconds(T3_5);
|
||||||
|
|
||||||
|
digitalWrite(TxEnablePin, LOW);
|
||||||
|
}
|
63
SimpleModbusSlave.h
Normal file
63
SimpleModbusSlave.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#ifndef SIMPLE_MODBUS_SLAVE_H
|
||||||
|
#define SIMPLE_MODBUS_SLAVE_H
|
||||||
|
|
||||||
|
// SimpleModbusSlaveV7
|
||||||
|
|
||||||
|
/*
|
||||||
|
SimpleModbusSlave allows you to communicate
|
||||||
|
to any slave using the Modbus RTU protocol.
|
||||||
|
|
||||||
|
This implementation DOES NOT fully comply with the Modbus specifications.
|
||||||
|
|
||||||
|
Specifically the frame time out have not been implemented according
|
||||||
|
to Modbus standards. The code does however combine the check for
|
||||||
|
inter character time out and frame time out by incorporating a maximum
|
||||||
|
time out allowable when reading from the message stream.
|
||||||
|
|
||||||
|
SimpleModbusSlave implements an unsigned int return value on a call to modbus_update().
|
||||||
|
This value is the total error count since the slave started. It's useful for fault finding.
|
||||||
|
|
||||||
|
This code is for a Modbus slave implementing functions 3 and 16
|
||||||
|
function 3: Reads the binary contents of holding registers (4X references)
|
||||||
|
function 16: Presets values into a sequence of holding registers (4X references)
|
||||||
|
|
||||||
|
All the functions share the same register array.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
The Arduino serial ring buffer is 128 bytes or 64 registers.
|
||||||
|
Most of the time you will connect the arduino to a master via serial
|
||||||
|
using a MAX485 or similar.
|
||||||
|
|
||||||
|
In a function 3 request the master will attempt to read from your
|
||||||
|
slave and since 5 bytes is already used for ID, FUNCTION, NO OF BYTES
|
||||||
|
and two BYTES CRC the master can only request 122 bytes or 61 registers.
|
||||||
|
|
||||||
|
In a function 16 request the master will attempt to write to your
|
||||||
|
slave and since a 9 bytes is already used for ID, FUNCTION, ADDRESS,
|
||||||
|
NO OF REGISTERS, NO OF BYTES and two BYTES CRC the master can only write
|
||||||
|
118 bytes or 59 registers.
|
||||||
|
|
||||||
|
Using a USB to Serial converter the maximum bytes you can send is
|
||||||
|
limited to its internal buffer which differs between manufactures.
|
||||||
|
|
||||||
|
The functions included here have been derived from the
|
||||||
|
Modbus Specifications and Implementation Guides
|
||||||
|
|
||||||
|
http://www.modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
|
||||||
|
http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
|
||||||
|
http://www.modbus.org/docs/PI_MBUS_300.pdf
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
// function definitions
|
||||||
|
unsigned int modbus_update();
|
||||||
|
void modbus_configure(HardwareSerial *SerialPort,
|
||||||
|
long baud,
|
||||||
|
unsigned char byteFormat,
|
||||||
|
unsigned char _slaveID,
|
||||||
|
unsigned char _TxEnablePin,
|
||||||
|
unsigned int _holdingRegsSize,
|
||||||
|
unsigned int* _regs);
|
||||||
|
|
||||||
|
#endif
|
Reference in New Issue
Block a user