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