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.

Committer:
gugani
Date:
Tue Oct 10 12:22:31 2017 +0000
Revision:
0:7c5265097fd2
Child:
1:85e226914fb7
Initial Commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
gugani 0:7c5265097fd2 1 #include "mbed.h"
gugani 0:7c5265097fd2 2 #include "lib_crc.h"
gugani 0:7c5265097fd2 3 #include "BufferedSerial.h"
gugani 0:7c5265097fd2 4 #include "SimpleModbusSlave.h"
gugani 0:7c5265097fd2 5
gugani 0:7c5265097fd2 6 #define BUFFER_SIZE 256
gugani 0:7c5265097fd2 7
gugani 0:7c5265097fd2 8 // frame[] is used to recieve and transmit packages.
gugani 0:7c5265097fd2 9 // The maximum serial ring buffer size is 256
gugani 0:7c5265097fd2 10 char frame[BUFFER_SIZE];
gugani 0:7c5265097fd2 11 unsigned int holdingRegsSize; // size of the register array
gugani 0:7c5265097fd2 12 char broadcastFlag;
gugani 0:7c5265097fd2 13 char slaveID;
gugani 0:7c5265097fd2 14 char function;
gugani 0:7c5265097fd2 15 char TxEnablePin;
gugani 0:7c5265097fd2 16 unsigned int errorCount = 0;
gugani 0:7c5265097fd2 17 unsigned int T1;
gugani 0:7c5265097fd2 18 unsigned int T1_5; // inter character time out
gugani 0:7c5265097fd2 19 unsigned int T3_5; // frame delay
gugani 0:7c5265097fd2 20 unsigned char buffer = 0;
gugani 0:7c5265097fd2 21
gugani 0:7c5265097fd2 22 Timer intercharacter_timer;
gugani 0:7c5265097fd2 23 Timeout interframe_timeout;
gugani 0:7c5265097fd2 24
gugani 0:7c5265097fd2 25
gugani 0:7c5265097fd2 26 DigitalOut dir(D4);
gugani 0:7c5265097fd2 27 BufferedSerial pc(PA_9, PA_10); //tx,rx
gugani 0:7c5265097fd2 28 Serial usbdebug(PA_2, PA_3); //tx,rx
gugani 0:7c5265097fd2 29
gugani 0:7c5265097fd2 30 // function definitions
gugani 0:7c5265097fd2 31 void exceptionResponse(unsigned char exception);
gugani 0:7c5265097fd2 32 unsigned int calculateCRC(char bufferSize);
gugani 0:7c5265097fd2 33 void sendPacket(unsigned char bufferSize);
gugani 0:7c5265097fd2 34 unsigned int reverse_CRC(unsigned int calculated_CRC);
gugani 0:7c5265097fd2 35
gugani 0:7c5265097fd2 36 void end_of_transmission()
gugani 0:7c5265097fd2 37 {
gugani 0:7c5265097fd2 38 if (TxEnablePin > 1)
gugani 0:7c5265097fd2 39 dir = 0;
gugani 0:7c5265097fd2 40 }
gugani 0:7c5265097fd2 41
gugani 0:7c5265097fd2 42 unsigned int modbus_update(unsigned int *holdingRegs)
gugani 0:7c5265097fd2 43 {
gugani 0:7c5265097fd2 44 unsigned char overflow = 0;
gugani 0:7c5265097fd2 45
gugani 0:7c5265097fd2 46 if (pc.readable()) {
gugani 0:7c5265097fd2 47 //while (pc.readable()){
gugani 0:7c5265097fd2 48 //usbdebug.printf("Serial data...\r\n");
gugani 0:7c5265097fd2 49 // The maximum number of bytes is limited to the serial buffer size of 128 bytes
gugani 0:7c5265097fd2 50 // If more bytes is received than the BUFFER_SIZE the overflow flag will be set and the
gugani 0:7c5265097fd2 51 // serial buffer will be red untill all the data is cleared from the receive buffer.
gugani 0:7c5265097fd2 52 if (overflow) {
gugani 0:7c5265097fd2 53 pc.getc();
gugani 0:7c5265097fd2 54 usbdebug.printf("Overflow\n\r");
gugani 0:7c5265097fd2 55 } else {
gugani 0:7c5265097fd2 56 if (buffer == BUFFER_SIZE)
gugani 0:7c5265097fd2 57 overflow = 1;
gugani 0:7c5265097fd2 58 frame[buffer] = pc.getc();
gugani 0:7c5265097fd2 59 buffer++;
gugani 0:7c5265097fd2 60 }
gugani 0:7c5265097fd2 61 intercharacter_timer.reset();
gugani 0:7c5265097fd2 62 }
gugani 0:7c5265097fd2 63
gugani 0:7c5265097fd2 64
gugani 0:7c5265097fd2 65 if (intercharacter_timer.read_us() > T1_5)
gugani 0:7c5265097fd2 66 {
gugani 0:7c5265097fd2 67 // If an overflow occurred increment the errorCount
gugani 0:7c5265097fd2 68 // variable and return to the main sketch without
gugani 0:7c5265097fd2 69 // responding to the request i.e. force a timeout
gugani 0:7c5265097fd2 70 if (overflow)
gugani 0:7c5265097fd2 71 return errorCount++;
gugani 0:7c5265097fd2 72
gugani 0:7c5265097fd2 73 // The minimum request packet is 8 bytes for function 3 & 16
gugani 0:7c5265097fd2 74 if (buffer > 6)
gugani 0:7c5265097fd2 75 {
gugani 0:7c5265097fd2 76 unsigned char id = frame[0];
gugani 0:7c5265097fd2 77
gugani 0:7c5265097fd2 78 broadcastFlag = 0;
gugani 0:7c5265097fd2 79
gugani 0:7c5265097fd2 80 if (id == 0)
gugani 0:7c5265097fd2 81 broadcastFlag = 1;
gugani 0:7c5265097fd2 82
gugani 0:7c5265097fd2 83 if (id == slaveID || broadcastFlag) // if the recieved ID matches the slaveID or broadcasting id (0), continue
gugani 0:7c5265097fd2 84 {
gugani 0:7c5265097fd2 85 unsigned int crc = ((frame[buffer - 2] << 8) | frame[buffer - 1]); // combine the crc Low & High bytes
gugani 0:7c5265097fd2 86 if (reverse_CRC(calculate_crc16_Modbus(frame, buffer - 2)) == crc) // if the calculated crc matches the recieved crc continue
gugani 0:7c5265097fd2 87 {
gugani 0:7c5265097fd2 88 function = frame[1];
gugani 0:7c5265097fd2 89 unsigned int startingAddress = ((frame[2] << 8) | frame[3]); // combine the starting address bytes
gugani 0:7c5265097fd2 90 unsigned int no_of_registers = ((frame[4] << 8) | frame[5]); // combine the number of register bytes
gugani 0:7c5265097fd2 91 unsigned int maxData = startingAddress + no_of_registers;
gugani 0:7c5265097fd2 92 unsigned char index;
gugani 0:7c5265097fd2 93 unsigned char address;
gugani 0:7c5265097fd2 94 unsigned int crc16;
gugani 0:7c5265097fd2 95
gugani 0:7c5265097fd2 96 // broadcasting is not supported for function 3
gugani 0:7c5265097fd2 97 if (!broadcastFlag && (function == 3))
gugani 0:7c5265097fd2 98 {
gugani 0:7c5265097fd2 99 if (startingAddress < holdingRegsSize) // check exception 2 ILLEGAL DATA ADDRESS
gugani 0:7c5265097fd2 100 {
gugani 0:7c5265097fd2 101 if (maxData <= holdingRegsSize) // check exception 3 ILLEGAL DATA VALUE
gugani 0:7c5265097fd2 102 {
gugani 0:7c5265097fd2 103 unsigned char noOfBytes = no_of_registers * 2;
gugani 0:7c5265097fd2 104 unsigned char responseFrameSize = 5 + noOfBytes; // ID, function, noOfBytes, (dataLo + dataHi) * number of registers, crcLo, crcHi
gugani 0:7c5265097fd2 105 frame[0] = slaveID;
gugani 0:7c5265097fd2 106 frame[1] = function;
gugani 0:7c5265097fd2 107 frame[2] = noOfBytes;
gugani 0:7c5265097fd2 108 address = 3; // PDU starts at the 4th byte
gugani 0:7c5265097fd2 109 unsigned int temp;
gugani 0:7c5265097fd2 110
gugani 0:7c5265097fd2 111 for (index = startingAddress; index < maxData; index++)
gugani 0:7c5265097fd2 112 {
gugani 0:7c5265097fd2 113 temp = holdingRegs[index];
gugani 0:7c5265097fd2 114 frame[address] = temp >> 8; // split the register into 2 bytes
gugani 0:7c5265097fd2 115 address++;
gugani 0:7c5265097fd2 116 frame[address] = temp & 0xFF;
gugani 0:7c5265097fd2 117 address++;
gugani 0:7c5265097fd2 118 }
gugani 0:7c5265097fd2 119
gugani 0:7c5265097fd2 120 crc16 = reverse_CRC(calculate_crc16_Modbus(frame, responseFrameSize - 2));
gugani 0:7c5265097fd2 121 frame[responseFrameSize - 2] = crc16 >> 8; // split crc into 2 bytes
gugani 0:7c5265097fd2 122 frame[responseFrameSize - 1] = crc16 & 0xFF;
gugani 0:7c5265097fd2 123 sendPacket(responseFrameSize);
gugani 0:7c5265097fd2 124 }
gugani 0:7c5265097fd2 125 else
gugani 0:7c5265097fd2 126 exceptionResponse(3); // exception 3 ILLEGAL DATA VALUE
gugani 0:7c5265097fd2 127 }
gugani 0:7c5265097fd2 128 else
gugani 0:7c5265097fd2 129 exceptionResponse(2); // exception 2 ILLEGAL DATA ADDRESS
gugani 0:7c5265097fd2 130 }
gugani 0:7c5265097fd2 131 else if (function == 6)
gugani 0:7c5265097fd2 132 {
gugani 0:7c5265097fd2 133 if (startingAddress < holdingRegsSize) // check exception 2 ILLEGAL DATA ADDRESS
gugani 0:7c5265097fd2 134 {
gugani 0:7c5265097fd2 135 unsigned int startingAddress = ((frame[2] << 8) | frame[3]);
gugani 0:7c5265097fd2 136 unsigned int regStatus = ((frame[4] << 8) | frame[5]);
gugani 0:7c5265097fd2 137 unsigned char responseFrameSize = 8;
gugani 0:7c5265097fd2 138
gugani 0:7c5265097fd2 139 holdingRegs[startingAddress] = regStatus;
gugani 0:7c5265097fd2 140
gugani 0:7c5265097fd2 141 //crc16 = calculateCRC(responseFrameSize - 2);
gugani 0:7c5265097fd2 142 crc16 = reverse_CRC(calculate_crc16_Modbus(frame, responseFrameSize - 2));
gugani 0:7c5265097fd2 143 frame[responseFrameSize - 2] = crc16 >> 8; // split crc into 2 bytes
gugani 0:7c5265097fd2 144 frame[responseFrameSize - 1] = crc16 & 0xFF;
gugani 0:7c5265097fd2 145 sendPacket(responseFrameSize);
gugani 0:7c5265097fd2 146 }
gugani 0:7c5265097fd2 147 else
gugani 0:7c5265097fd2 148 exceptionResponse(2); // exception 2 ILLEGAL DATA ADDRESS
gugani 0:7c5265097fd2 149 }
gugani 0:7c5265097fd2 150 else if (function == 16)
gugani 0:7c5265097fd2 151 {
gugani 0:7c5265097fd2 152 // check if the recieved number of bytes matches the calculated bytes minus the request bytes
gugani 0:7c5265097fd2 153 // id + function + (2 * address bytes) + (2 * no of register bytes) + byte count + (2 * CRC bytes) = 9 bytes
gugani 0:7c5265097fd2 154 if (frame[6] == (buffer - 9))
gugani 0:7c5265097fd2 155 {
gugani 0:7c5265097fd2 156 if (startingAddress < holdingRegsSize) // check exception 2 ILLEGAL DATA ADDRESS
gugani 0:7c5265097fd2 157 {
gugani 0:7c5265097fd2 158 if (maxData <= holdingRegsSize) // check exception 3 ILLEGAL DATA VALUE
gugani 0:7c5265097fd2 159 {
gugani 0:7c5265097fd2 160 address = 7; // start at the 8th byte in the frame
gugani 0:7c5265097fd2 161
gugani 0:7c5265097fd2 162 for (index = startingAddress; index < maxData; index++)
gugani 0:7c5265097fd2 163 {
gugani 0:7c5265097fd2 164 holdingRegs[index] = ((frame[address] << 8) | frame[address + 1]);
gugani 0:7c5265097fd2 165 address += 2;
gugani 0:7c5265097fd2 166 }
gugani 0:7c5265097fd2 167
gugani 0:7c5265097fd2 168 // only the first 6 bytes are used for CRC calculation
gugani 0:7c5265097fd2 169 //crc16 = calculateCRC(6);
gugani 0:7c5265097fd2 170 crc16 = reverse_CRC(calculate_crc16_Modbus(frame, 6));
gugani 0:7c5265097fd2 171 frame[6] = crc16 >> 8; // split crc into 2 bytes
gugani 0:7c5265097fd2 172 frame[7] = crc16 & 0xFF;
gugani 0:7c5265097fd2 173
gugani 0:7c5265097fd2 174 // a function 16 response is an echo of the first 6 bytes from the request + 2 crc bytes
gugani 0:7c5265097fd2 175 if (!broadcastFlag) // don't respond if it's a broadcast message
gugani 0:7c5265097fd2 176 sendPacket(8);
gugani 0:7c5265097fd2 177 }
gugani 0:7c5265097fd2 178 else
gugani 0:7c5265097fd2 179 exceptionResponse(3); // exception 3 ILLEGAL DATA VALUE
gugani 0:7c5265097fd2 180 }
gugani 0:7c5265097fd2 181 else
gugani 0:7c5265097fd2 182 exceptionResponse(2); // exception 2 ILLEGAL DATA ADDRESS
gugani 0:7c5265097fd2 183 }
gugani 0:7c5265097fd2 184 else {
gugani 0:7c5265097fd2 185 errorCount++; // corrupted packet
gugani 0:7c5265097fd2 186 usbdebug.printf("The recieved number of bytes does not match the calculated bytes minus the request bytes\r\n");
gugani 0:7c5265097fd2 187 }
gugani 0:7c5265097fd2 188 }
gugani 0:7c5265097fd2 189 else
gugani 0:7c5265097fd2 190 exceptionResponse(1); // exception 1 ILLEGAL FUNCTION
gugani 0:7c5265097fd2 191 }
gugani 0:7c5265097fd2 192 else // checksum failed
gugani 0:7c5265097fd2 193 {
gugani 0:7c5265097fd2 194 errorCount++;usbdebug.printf("Checksum failed\n\r");
gugani 0:7c5265097fd2 195 }
gugani 0:7c5265097fd2 196 } // incorrect id
gugani 0:7c5265097fd2 197 }
gugani 0:7c5265097fd2 198 else if (buffer > 0 && buffer < 8)
gugani 0:7c5265097fd2 199 {
gugani 0:7c5265097fd2 200 errorCount++; // corrupted packet
gugani 0:7c5265097fd2 201 usbdebug.printf("Packet < 8 bytes\r\n");
gugani 0:7c5265097fd2 202 }
gugani 0:7c5265097fd2 203 buffer = 0;
gugani 0:7c5265097fd2 204 //usbdebug.printf("Error count: %d\n\r", errorCount);
gugani 0:7c5265097fd2 205 }
gugani 0:7c5265097fd2 206 return errorCount;
gugani 0:7c5265097fd2 207 }
gugani 0:7c5265097fd2 208
gugani 0:7c5265097fd2 209 void exceptionResponse(unsigned char exception)
gugani 0:7c5265097fd2 210 {
gugani 0:7c5265097fd2 211 errorCount++; // each call to exceptionResponse() will increment the errorCount
gugani 0:7c5265097fd2 212 if (!broadcastFlag) // don't respond if its a broadcast message
gugani 0:7c5265097fd2 213 {
gugani 0:7c5265097fd2 214 frame[0] = slaveID;
gugani 0:7c5265097fd2 215 frame[1] = (function | 0x80); // set the MSB bit high, informs the master of an exception
gugani 0:7c5265097fd2 216 frame[2] = exception;
gugani 0:7c5265097fd2 217 unsigned int crc16 = reverse_CRC(calculate_crc16_Modbus(frame, 3));
gugani 0:7c5265097fd2 218 frame[3] = crc16 >> 8;
gugani 0:7c5265097fd2 219 frame[4] = crc16 & 0xFF;
gugani 0:7c5265097fd2 220 sendPacket(5); // exception response is always 5 bytes ID, function + 0x80, exception code, 2 bytes crc
gugani 0:7c5265097fd2 221 }
gugani 0:7c5265097fd2 222 }
gugani 0:7c5265097fd2 223
gugani 0:7c5265097fd2 224 void modbus_configure(long baud, char _slaveID, char _TxEnablePin, unsigned int _holdingRegsSize, unsigned char _lowLatency)
gugani 0:7c5265097fd2 225 {
gugani 0:7c5265097fd2 226 slaveID = _slaveID;
gugani 0:7c5265097fd2 227 pc.baud(baud);
gugani 0:7c5265097fd2 228 usbdebug.baud(115200);
gugani 0:7c5265097fd2 229 usbdebug.printf("modbus_configure\r\n");
gugani 0:7c5265097fd2 230 usbdebug.printf("Baudrate: %d, Slave id: %d\r\n", baud, slaveID);
gugani 0:7c5265097fd2 231
gugani 0:7c5265097fd2 232 if (_TxEnablePin > 1)
gugani 0:7c5265097fd2 233 { // pin 0 & pin 1 are reserved for RX/TX. To disable set txenpin < 2
gugani 0:7c5265097fd2 234 TxEnablePin = _TxEnablePin;
gugani 0:7c5265097fd2 235 dir = 0;
gugani 0:7c5265097fd2 236 }
gugani 0:7c5265097fd2 237
gugani 0:7c5265097fd2 238 // Modbus states that a baud rate higher than 19200 must use a fixed 750 us
gugani 0:7c5265097fd2 239 // for inter character time out and 1.75 ms for a frame delay.
gugani 0:7c5265097fd2 240 // For baud rates below 19200 the timeing is more critical and has to be calculated.
gugani 0:7c5265097fd2 241 // E.g. 9600 baud in a 10 bit packet is 960 characters per second
gugani 0:7c5265097fd2 242 // In milliseconds this will be 960characters per 1000ms. So for 1 character
gugani 0:7c5265097fd2 243 // 1000ms/960characters is 1.04167ms per character and finaly modbus states an
gugani 0:7c5265097fd2 244 // intercharacter must be 1.5T or 1.5 times longer than a normal character and thus
gugani 0:7c5265097fd2 245 // 1.5T = 1.04167ms * 1.5 = 1.5625ms. A frame delay is 3.5T.
gugani 0:7c5265097fd2 246 // Added experimental low latency delays. This makes the implementation
gugani 0:7c5265097fd2 247 // non-standard but practically it works with all major modbus master implementations.
gugani 0:7c5265097fd2 248
gugani 0:7c5265097fd2 249 if (baud == 1000000 && _lowLatency){
gugani 0:7c5265097fd2 250 T1_5 = 1;
gugani 0:7c5265097fd2 251 T3_5 = 10;
gugani 0:7c5265097fd2 252 }
gugani 0:7c5265097fd2 253 else if (baud >= 115200 && _lowLatency){
gugani 0:7c5265097fd2 254 T1_5 = 75;
gugani 0:7c5265097fd2 255 T3_5 = 175;
gugani 0:7c5265097fd2 256 }
gugani 0:7c5265097fd2 257 else if (baud > 19200){
gugani 0:7c5265097fd2 258 T1_5 = 750;
gugani 0:7c5265097fd2 259 T3_5 = 1750;
gugani 0:7c5265097fd2 260 }
gugani 0:7c5265097fd2 261 else {
gugani 0:7c5265097fd2 262 T1_5 = 15000000/baud; // 1T * 1.5 = T1.5
gugani 0:7c5265097fd2 263 T3_5 = 35000000/baud; // 1T * 3.5 = T3.5
gugani 0:7c5265097fd2 264 }
gugani 0:7c5265097fd2 265 T1 = 10000000/baud;
gugani 0:7c5265097fd2 266 usbdebug.printf("T1 = %d T1_5 = %d T3_5 = %d\r\n",T1, T1_5, T3_5);
gugani 0:7c5265097fd2 267
gugani 0:7c5265097fd2 268 holdingRegsSize = _holdingRegsSize;
gugani 0:7c5265097fd2 269 errorCount = 0; // initialize errorCount
gugani 0:7c5265097fd2 270
gugani 0:7c5265097fd2 271 intercharacter_timer.start();
gugani 0:7c5265097fd2 272 }
gugani 0:7c5265097fd2 273
gugani 0:7c5265097fd2 274 void sendPacket(unsigned char bufferSize)
gugani 0:7c5265097fd2 275 {
gugani 0:7c5265097fd2 276 if (TxEnablePin > 1)
gugani 0:7c5265097fd2 277 dir = 1;
gugani 0:7c5265097fd2 278
gugani 0:7c5265097fd2 279 for (unsigned char i = 0; i < bufferSize; i++)
gugani 0:7c5265097fd2 280 pc.putc(frame[i]);
gugani 0:7c5265097fd2 281
gugani 0:7c5265097fd2 282 // allow a frame delay to indicate end of transmission and wait T3_5
gugani 0:7c5265097fd2 283 interframe_timeout.attach_us(&end_of_transmission, T1 * bufferSize + T3_5);
gugani 0:7c5265097fd2 284 }
gugani 0:7c5265097fd2 285
gugani 0:7c5265097fd2 286 unsigned int reverse_CRC(unsigned int calculated_CRC){
gugani 0:7c5265097fd2 287 unsigned int temp;
gugani 0:7c5265097fd2 288 temp = calculated_CRC >> 8;
gugani 0:7c5265097fd2 289 calculated_CRC = (calculated_CRC << 8) | temp;
gugani 0:7c5265097fd2 290 calculated_CRC &= 0xFFFF;
gugani 0:7c5265097fd2 291 return calculated_CRC;
gugani 0:7c5265097fd2 292 }