SimpleModbusSlave allows you to communicate to any slave using the Modbus RTU protocol. The crc calculation is based on https://os.mbed.com/users/jpelletier/code/CRC/ The functions implemented are functions 3 and 16. read holding registers and preset multiple registers of the Modbus RTU Protocol. This implementation DOES NOT fully comply with the Modbus specifications.

Revision:
0:7c5265097fd2
Child:
1:85e226914fb7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SimpleModbusSlave.cpp	Tue Oct 10 12:22:31 2017 +0000
@@ -0,0 +1,292 @@
+#include "mbed.h"
+#include "lib_crc.h"
+#include "BufferedSerial.h"
+#include "SimpleModbusSlave.h"
+
+#define BUFFER_SIZE 256
+
+// frame[] is used to recieve and transmit packages. 
+// The maximum serial ring buffer size is 256
+char frame[BUFFER_SIZE];
+unsigned int holdingRegsSize; // size of the register array 
+char broadcastFlag;
+char slaveID;
+char function;
+char TxEnablePin;
+unsigned int errorCount = 0;
+unsigned int T1;
+unsigned int T1_5; // inter character time out
+unsigned int T3_5; // frame delay
+unsigned char buffer = 0;
+
+Timer intercharacter_timer;
+Timeout interframe_timeout;
+
+
+DigitalOut dir(D4);
+BufferedSerial pc(PA_9, PA_10); //tx,rx
+Serial usbdebug(PA_2, PA_3); //tx,rx
+
+// function definitions
+void exceptionResponse(unsigned char exception);
+unsigned int calculateCRC(char bufferSize); 
+void sendPacket(unsigned char bufferSize);
+unsigned int reverse_CRC(unsigned int calculated_CRC);
+
+void end_of_transmission()
+{
+    if (TxEnablePin > 1)
+        dir = 0;
+}
+
+unsigned int modbus_update(unsigned int *holdingRegs)
+{
+    unsigned char overflow = 0;    
+
+    if (pc.readable()) {
+        //while (pc.readable()){
+        //usbdebug.printf("Serial data...\r\n");
+        // 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) {
+            pc.getc();
+            usbdebug.printf("Overflow\n\r");
+        } else {
+            if (buffer == BUFFER_SIZE)
+                overflow = 1;
+            frame[buffer] = pc.getc();            
+            buffer++;
+        }
+        intercharacter_timer.reset();        
+    }
+    
+    
+    if (intercharacter_timer.read_us() > T1_5)
+    {           
+        // 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 > 6) 
+        {            
+            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 (reverse_CRC(calculate_crc16_Modbus(frame, 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;
+                                unsigned char responseFrameSize = 5 + noOfBytes; // ID, function, noOfBytes, (dataLo + dataHi) * number of registers, crcLo, crcHi
+                                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 = holdingRegs[index];
+                                    frame[address] = temp >> 8; // split the register into 2 bytes
+                                    address++;
+                                    frame[address] = temp & 0xFF;
+                                    address++;
+                                } 
+                                              
+                                crc16 = reverse_CRC(calculate_crc16_Modbus(frame, 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 == 6)
+                    {
+                        if (startingAddress < holdingRegsSize) // check exception 2 ILLEGAL DATA ADDRESS
+                        {
+                            unsigned int startingAddress = ((frame[2] << 8) | frame[3]);
+                            unsigned int regStatus = ((frame[4] << 8) | frame[5]);
+                            unsigned char responseFrameSize = 8;
+                            
+                            holdingRegs[startingAddress] = regStatus;
+                            
+                            //crc16 = calculateCRC(responseFrameSize - 2);
+                            crc16 = reverse_CRC(calculate_crc16_Modbus(frame, responseFrameSize - 2));
+                            frame[responseFrameSize - 2] = crc16 >> 8; // split crc into 2 bytes
+                            frame[responseFrameSize - 1] = crc16 & 0xFF;
+                            sendPacket(responseFrameSize);
+                        }
+                        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++)
+                                    {
+                                        holdingRegs[index] = ((frame[address] << 8) | frame[address + 1]);
+                                        address += 2;
+                                    }
+            
+                                    // only the first 6 bytes are used for CRC calculation
+                                    //crc16 = calculateCRC(6);
+                                    crc16 = reverse_CRC(calculate_crc16_Modbus(frame, 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
+                            usbdebug.printf("The recieved number of bytes does not match the calculated bytes minus the request bytes\r\n");
+                        }
+                    }         
+                        else
+                            exceptionResponse(1); // exception 1 ILLEGAL FUNCTION
+                }
+                else // checksum failed
+                {
+                    errorCount++;usbdebug.printf("Checksum failed\n\r");
+                }
+            } // incorrect id
+        }
+        else if (buffer > 0 && buffer < 8)
+        {
+            errorCount++; // corrupted packet
+            usbdebug.printf("Packet < 8 bytes\r\n");
+        }        
+        buffer = 0;
+        //usbdebug.printf("Error count: %d\n\r", errorCount);        
+    }
+    return errorCount; 
+}  
+
+void exceptionResponse(unsigned char exception)
+{
+  errorCount++; // each call to exceptionResponse() will increment the errorCount
+  if (!broadcastFlag) // don't respond if its a broadcast message
+  {
+    frame[0] = slaveID;
+    frame[1] = (function | 0x80); // set the MSB bit high, informs the master of an exception
+    frame[2] = exception;    
+    unsigned int crc16 = reverse_CRC(calculate_crc16_Modbus(frame, 3));
+    frame[3] = crc16 >> 8;
+    frame[4] = crc16 & 0xFF;
+    sendPacket(5); // exception response is always 5 bytes ID, function + 0x80, exception code, 2 bytes crc
+  }
+}
+
+void modbus_configure(long baud, char _slaveID, char _TxEnablePin, unsigned int _holdingRegsSize, unsigned char _lowLatency)
+{
+    slaveID = _slaveID;
+    pc.baud(baud);
+    usbdebug.baud(115200);
+    usbdebug.printf("modbus_configure\r\n");        
+    usbdebug.printf("Baudrate: %d, Slave id: %d\r\n", baud, slaveID);
+        
+    if (_TxEnablePin > 1) 
+    { // pin 0 & pin 1 are reserved for RX/TX. To disable set txenpin < 2
+        TxEnablePin = _TxEnablePin;     
+        dir = 0;
+    }
+    
+    // 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 timeing 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 finaly modbus states an
+    // intercharacter must be 1.5T or 1.5 times longer than a normal character and thus
+    // 1.5T = 1.04167ms * 1.5 = 1.5625ms. A frame delay is 3.5T.
+    // Added experimental low latency delays. This makes the implementation
+    // non-standard but practically it works with all major modbus master implementations.
+    
+    if (baud == 1000000 && _lowLatency){
+        T1_5 = 1; 
+        T3_5 = 10;
+    }
+    else if (baud >= 115200 && _lowLatency){
+        T1_5 = 75; 
+        T3_5 = 175; 
+    }
+    else 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
+    }
+    T1 = 10000000/baud;
+    usbdebug.printf("T1 = %d T1_5 = %d  T3_5 = %d\r\n",T1, T1_5, T3_5);
+    
+    holdingRegsSize = _holdingRegsSize;
+    errorCount = 0; // initialize errorCount
+    
+    intercharacter_timer.start();
+}   
+
+void sendPacket(unsigned char bufferSize)
+{
+  if (TxEnablePin > 1)
+    dir = 1;
+    
+  for (unsigned char i = 0; i < bufferSize; i++)
+    pc.putc(frame[i]);  
+  
+  // allow a frame delay to indicate end of transmission and wait T3_5  
+  interframe_timeout.attach_us(&end_of_transmission, T1 * bufferSize + T3_5);  
+}
+
+unsigned int reverse_CRC(unsigned int calculated_CRC){
+    unsigned int temp;
+    temp = calculated_CRC >> 8;
+    calculated_CRC = (calculated_CRC << 8) | temp;
+    calculated_CRC &= 0xFFFF;    
+    return calculated_CRC;
+}