Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 0:7a9f746fb93e, committed 2014-08-27
- Comitter:
- homayoun
- Date:
- Wed Aug 27 18:13:19 2014 +0000
- Commit message:
- Arduino SimpleModbusMaster modified for mbed
Changed in this revision
| SimpleModbus.cpp | Show annotated file Show diff for this revision Revisions of this file |
| SimpleModbus.h | Show annotated file Show diff for this revision Revisions of this file |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SimpleModbus.cpp Wed Aug 27 18:13:19 2014 +0000
@@ -0,0 +1,443 @@
+#include "mbed.h"
+#include "SimpleModbus.h"
+
+// state machine states
+#define IDLE 1
+#define WAITING_FOR_REPLY 2
+#define WAITING_FOR_TURNAROUND 3
+
+#define BUFFER_SIZE 64
+
+Timer ModBusTimer;
+Serial ModbusPort(SERIAL_TX, SERIAL_RX);
+
+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 64 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 int total_no_of_packets;
+Packet* packetArray; // packet starting address
+Packet* packet; // current packet
+
+// 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 (ModBusTimer.read_ms() > polling)
+ state = IDLE;
+}
+
+// get the serial data from the buffer
+void waiting_for_reply()
+{
+ if (ModbusPort.readable()) { // is there something to check?
+ unsigned char overflowFlag = 0;
+ buffer = 0;
+ while (ModbusPort.readable()) {
+ // 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.getc();
+ else {
+ if (buffer == BUFFER_SIZE)
+ overflowFlag = 1;
+
+ frame[buffer] = ModbusPort.getc();
+ 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.
+ wait_ms(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 (ModBusTimer.read_ms() > 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 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;
+ }
+ state = WAITING_FOR_TURNAROUND;
+ ModBusTimer.start(); // 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;
+ ModBusTimer.start(); // start the turnaround delay
+}
+
+void modbus_configure(unsigned int _timeout,
+ unsigned int _polling,
+ unsigned char _retry_count,
+ 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
+
+
+ T1_5 = 750;
+
+ // initialize
+ state = IDLE;
+ timeout = _timeout;
+ polling = _polling;
+ retry_count = _retry_count;
+ total_no_of_packets = _total_no_of_packets;
+ packetArray = _packets;
+
+ ModbusPort.baud(9600);
+ ModbusPort.format(8,SerialBase::None,2);
+}
+
+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;
+}
+
+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)
+{
+
+
+ for (unsigned char i = 0; i < bufferSize; i++)
+ ModbusPort.putc(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
+
+ ModBusTimer.start(); // start the timeout delay
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SimpleModbus.h Wed Aug 27 18:13:19 2014 +0000
@@ -0,0 +1,129 @@
+#ifndef SIMPLE_MODBUS_MASTER_H
+#define SIMPLE_MODBUS_MASTER_H
+
+// SimpleModbusMasterV12
+
+/*
+ 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 64 bytes or 32 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 58 bytes or 29 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
+ 54 bytes or 27 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 64 bytes.
+*/
+
+#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;
+
+} 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(unsigned int _timeout,
+ unsigned int _polling,
+ unsigned char _retry_count,
+ Packet* _packets,
+ unsigned int _total_no_of_packets);
+
+#endif
\ No newline at end of file