Lydia - Printhead
ModbusRtu.h
Go to the documentation of this file.
1 
40 #include <inttypes.h>
41 #include "Arduino.h"
42 #include "Print.h"
43 #include <SoftwareSerial.h>
44 #include "enums.h"
45 #include "./utils.h"
46 
47 #define RS485_PIN_DE 0b01000000
48 #define RS485_PIN_RE 0b00100000
49 #define RS485_DIR_REG DDRJ
50 #define RS485_PORT_REG PORTJ
51 #define RS485_CLEAR_DE RS485_PORT_REG &= ~RS485_PIN_DE
52 #define RS485_CLEAR_RE RS485_PORT_REG &= ~RS485_PIN_RE
53 #define RS485_SET_DE RS485_PORT_REG |= RS485_PIN_DE
54 #define RS485_SET_RE RS485_PORT_REG |= RS485_PIN_RE
55 
56 #define T35 5
57 #define MAX_BUFFER 64
58 
59 
67 typedef struct
68 {
69  uint8_t u8id;
70  uint8_t u8fct;
71  uint16_t u16RegAdd;
72  uint16_t u16CoilsNo;
73  uint16_t *au16reg;
74  uint8_t flags;
75  bool coilValue;
76 
77 } modbus_t;
78 
79 enum
80 {
84 };
85 
91 enum MESSAGE
92 {
93  ID = 0,
94  FUNC,
100 };
101 
112 {
113  COM_IDLE = 0,
115 };
116 
118 {
124 };
125 
126 enum
127 {
128  NO_REPLY = 255,
133 };
134 
135 const unsigned char fctsupported[] =
136  {
137  MB_FC_READ_COILS,
138  MB_FC_READ_DISCRETE_INPUT,
139  MB_FC_READ_REGISTERS,
140  MB_FC_READ_INPUT_REGISTER,
141  MB_FC_WRITE_COIL,
142  MB_FC_WRITE_REGISTER,
143  MB_FC_WRITE_MULTIPLE_COILS,
144  MB_FC_WRITE_MULTIPLE_REGISTERS};
145 
152 class Modbus
153 {
154 private:
155  HardwareSerial *port;
156  SoftwareSerial *softPort;
157  uint8_t u8id;
158  uint8_t u8serno;
159  uint8_t u8txenpin;
160  uint8_t u8state;
161  uint8_t u8lastError;
163  uint8_t u8BufferSize;
164  uint8_t u8lastRec;
165  uint16_t *au16regs;
167  uint16_t u16timeOut;
168  uint32_t u32time, u32timeOut;
169  uint8_t u8regsize;
170 
171  void init(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin);
172  void init(uint8_t u8id);
173  void sendTxBuffer();
174  int8_t getRxBuffer();
175  uint16_t calcCRC(uint8_t u8length);
176  uint8_t validateAnswer();
177  uint8_t validateRequest();
179  void get_FC1();
180  void get_FC3();
181  int8_t process_FC1(uint16_t *regs, uint8_t u8size);
182  int8_t process_FC3(uint16_t *regs, uint8_t u8size);
183  int8_t process_FC5(uint16_t *regs, uint8_t u8size);
184  int8_t process_FC6(uint16_t *regs, uint8_t u8size);
185  int8_t process_FC15(uint16_t *regs, uint8_t u8size);
186  int8_t process_FC16(uint16_t *regs, uint8_t u8size);
187  void buildException(uint8_t u8exception); // build exception message
188 
189 public:
190  Modbus();
191  Modbus(uint8_t u8id, uint8_t u8serno);
192  Modbus(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin);
193  Modbus(uint8_t u8id);
194  void begin(long u32speed);
195  void begin(SoftwareSerial *sPort, long u32speed);
196  void begin(long u32speed, uint8_t u8config);
197  void begin();
198  void setTimeOut(uint16_t u16timeout);
199  uint16_t getTimeOut();
200  boolean getTimeOutState();
201  int8_t query(modbus_t telegram);
202  int8_t poll();
203  int8_t poll(uint16_t *regs, uint8_t u8size);
204  uint16_t getInCnt();
205  uint16_t getOutCnt();
206  uint16_t getErrCnt();
207  uint8_t getID();
208  uint8_t getState();
209  uint8_t getLastError();
210  void setID(uint8_t u8id);
211  void end();
212  bool isLocked();
213  void clearError();
214  bool debugSend;
215 
216  uint8_t rxSize;
218 };
219 
220 /* _____PUBLIC FUNCTIONS_____________________________________________________ */
221 
229 {
230  init(0, 0, 0);
231  debugSend = false;
232 }
233 
245 Modbus::Modbus(uint8_t u8id, uint8_t u8serno)
246 {
247  init(u8id, u8serno, 0);
248 }
249 
263 Modbus::Modbus(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin)
264 {
266 }
267 
282 Modbus::Modbus(uint8_t u8id)
283 {
284  init(u8id);
285 }
286 
298 void Modbus::begin(long u32speed)
299 {
300 
301  switch (u8serno)
302  {
303 #if defined(UBRR1H)
304  case 1:
305  port = &Serial1;
306  break;
307 #endif
308 
309 #if defined(UBRR2H)
310  case 2:
311  port = &Serial2;
312  break;
313 #endif
314 
315 #if defined(UBRR3H)
316  case 3:
317  port = &Serial3;
318  break;
319 #endif
320  case 0:
321  default:
322 
323  port = &Serial;
324  break;
325  }
326 
327  port->begin(u32speed);
328  if (u8txenpin > 1) // pin 0 & pin 1 are reserved for RX/TX
329  {
330  // return RS485 transceiver to transmit mode
334  // pinMode(u8txenpin, OUTPUT);
335  // digitalWrite(u8txenpin, LOW);
336  }
337 
338  while (port->read() >= 0)
339  ;
340  u8lastRec = u8BufferSize = 0;
341  u16InCnt = u16OutCnt = u16errCnt = 0;
342 }
343 
345 {
347 }
348 
360 void Modbus::begin(SoftwareSerial *sPort, long u32speed)
361 {
362 
363  softPort = sPort;
364 
365  softPort->begin(u32speed);
366 
367  if (u8txenpin > 1) // pin 0 & pin 1 are reserved for RX/TX
368  {
369  // return RS485 transceiver to transmit mode
373  // pinMode(u8txenpin, OUTPUT);
374  // digitalWrite(u8txenpin, LOW);
375  }
376 
377  while (softPort->read() >= 0)
378  ;
379  u8lastRec = u8BufferSize = 0;
380  u16InCnt = u16OutCnt = u16errCnt = 0;
381 }
382 
395 void Modbus::begin(long u32speed, uint8_t u8config)
396 {
397 
398  switch (u8serno)
399  {
400 #if defined(UBRR1H)
401  case 1:
402  port = &Serial1;
403  break;
404 #endif
405 
406 #if defined(UBRR2H)
407  case 2:
408  port = &Serial2;
409  break;
410 #endif
411 
412 #if defined(UBRR3H)
413  case 3:
414  port = &Serial3;
415  break;
416 #endif
417  case 0:
418  default:
419  port = &Serial;
420  break;
421  }
422 
423  port->begin(u32speed, u8config);
424  if (u8txenpin > 1) // pin 0 & pin 1 are reserved for RX/TX
425  {
426  // return RS485 transceiver to transmit mode
430  // pinMode(u8txenpin, OUTPUT);
431  // digitalWrite(u8txenpin, LOW);
432  }
433 
434  while (port->read() >= 0)
435  ;
436  u8lastRec = u8BufferSize = 0;
437  u16InCnt = u16OutCnt = u16errCnt = 0;
438 }
439 
451 {
452  begin(19200);
453 }
454 
462 void Modbus::setID(uint8_t u8id)
463 {
464  if ((u8id != 0) && (u8id <= 247))
465  {
466  this->u8id = u8id;
467  }
468 }
469 
477 uint8_t Modbus::getID()
478 {
479  return this->u8id;
480 }
481 
493 void Modbus::setTimeOut(uint16_t u16timeOut)
494 {
495  this->u16timeOut = u16timeOut;
496 }
497 
507 {
508  return (millis() > u32timeOut);
509 }
510 
520 {
521  return u16InCnt;
522 }
523 
533 {
534  return u16OutCnt;
535 }
536 
546 {
547  return u16errCnt;
548 }
549 
551 {
552  u16errCnt = 0;
553 }
554 
562 {
563  return u8state;
564 }
565 
576 {
577  return u8lastError;
578 }
579 
592 int8_t Modbus::query(modbus_t telegram)
593 {
594 
595  uint8_t u8regsno, u8bytesno;
596  if (u8id != 0)
597  return -2;
598  if (u8state != COM_IDLE)
599  return -1;
600 
601  if ((telegram.u8id == 0) || (telegram.u8id > 247))
602  return -3;
603 
604  au16regs = telegram.au16reg;
605  telegram.flags = QUEUED;
606  currentQuery = &telegram;
607 
608  // telegram header
609  au8Buffer[ID] = telegram.u8id;
610  au8Buffer[FUNC] = telegram.u8fct;
611  au8Buffer[ADD_HI] = highByte(telegram.u16RegAdd);
612  au8Buffer[ADD_LO] = lowByte(telegram.u16RegAdd);
613 
614  switch (telegram.u8fct)
615  {
616  case MB_FC_READ_COILS:
617  case MB_FC_READ_DISCRETE_INPUT:
618  case MB_FC_READ_REGISTERS:
619  case MB_FC_READ_INPUT_REGISTER:
620  au8Buffer[NB_HI] = highByte(telegram.u16CoilsNo);
621  au8Buffer[NB_LO] = lowByte(telegram.u16CoilsNo);
622  u8BufferSize = 6;
623  break;
624  case MB_FC_WRITE_COIL:
625  au8Buffer[NB_HI] = ((telegram.u16CoilsNo > 0) ? 0xff : 0);
626  au8Buffer[NB_LO] = 0;
627  u8BufferSize = 6;
628  break;
629  case MB_FC_WRITE_REGISTER:
630  au8Buffer[NB_HI] = highByte(telegram.u16CoilsNo);
631  au8Buffer[NB_LO] = lowByte(telegram.u16CoilsNo);
632  u8BufferSize = 6;
633  break;
634  case MB_FC_WRITE_MULTIPLE_COILS: // TODO: implement "sending coils"
635  u8regsno = telegram.u16CoilsNo / 16;
636  u8bytesno = u8regsno * 2;
637  if ((telegram.u16CoilsNo % 16) != 0)
638  {
639  u8bytesno++;
640  u8regsno++;
641  }
642 
643  au8Buffer[NB_HI] = highByte(telegram.u16CoilsNo);
644  au8Buffer[NB_LO] = lowByte(telegram.u16CoilsNo);
645  au8Buffer[NB_LO + 1] = u8bytesno;
646  u8BufferSize = 7;
647 
648  u8regsno = u8bytesno = 0;
649  break;
650 
652  {
653 
654  break;
655  }
656  case MB_FC_WRITE_MULTIPLE_REGISTERS:
657  au8Buffer[NB_HI] = highByte(telegram.u16CoilsNo);
658  au8Buffer[NB_LO] = lowByte(telegram.u16CoilsNo);
659  au8Buffer[NB_LO + 1] = (uint8_t)(telegram.u16CoilsNo * 2);
660  u8BufferSize = 7;
661 
662  for (uint16_t i = 0; i < telegram.u16CoilsNo; i++)
663  {
664  au8Buffer[u8BufferSize] = highByte(au16regs[i]);
665  u8BufferSize++;
666  au8Buffer[u8BufferSize] = lowByte(au16regs[i]);
667  u8BufferSize++;
668  }
669  break;
670  }
671 
672  sendTxBuffer();
674  return 0;
675 }
676 
691 int8_t Modbus::poll()
692 {
693  // check if there is any incoming frame
694  uint8_t u8current;
695  if (u8serno < 4)
696  {
697  u8current = port->available();
698  }
699  else
700  {
701  u8current = softPort->available();
702  }
703 
704  if (millis() > u32timeOut)
705  {
706  u8state = COM_IDLE;
708  u16errCnt++;
709  return 0;
710  }
711 
712  if (u8current == 0)
713  {
714  return 0;
715  }
716 
717  // check T35 after frame end or still no frame end
718  if (u8current != u8lastRec)
719  {
720  u8lastRec = u8current;
721  u32time = millis() + T35;
722  return 0;
723  }
724  if (millis() < u32time)
725  {
726  return 0;
727  };
728 
729  // transfer Serial buffer frame to auBuffer
730  u8lastRec = 0;
731  int8_t i8state = getRxBuffer();
732  if (i8state < 7)
733  {
734  u8state = COM_IDLE;
735  u16errCnt++;
736  Log.verboseln("Add error : %d - RX-Buffer < 7", i8state);
738  return i8state;
739  }
740 
741  // validate message: id, CRC, FCT, exception
742  uint8_t u8exception = validateAnswer();
743  if (u8exception != 0)
744  {
745  Log.verboseln("ModbusRTU::poll : exception : %d", u8exception);
746  u8state = COM_IDLE;
747  return u8exception;
748  }
749 
750  // process answer
751  switch (au8Buffer[FUNC])
752  {
753  case MB_FC_READ_COILS:
754  case MB_FC_READ_DISCRETE_INPUT:
755  // call get_FC1 to transfer the incoming message to au16regs buffer
756  get_FC1();
757  break;
758  case MB_FC_READ_INPUT_REGISTER:
759  case MB_FC_READ_REGISTERS:
760  // call get_FC3 to transfer the incoming message to au16regs buffer
761  get_FC3();
762  break;
763  case MB_FC_WRITE_COIL:
764  case MB_FC_WRITE_REGISTER:
765  case MB_FC_WRITE_MULTIPLE_COILS:
766  case MB_FC_WRITE_MULTIPLE_REGISTERS:
767  // nothing to do
768  break;
769  default:
770  break;
771  }
772  u8state = COM_IDLE;
774  return u8BufferSize;
775 }
776 
790 int8_t Modbus::poll(uint16_t *regs, uint8_t u8size)
791 {
792 
793  au16regs = regs;
794  u8regsize = u8size;
795  uint8_t u8current;
796 
797  // check if there is any incoming frame
798  if (u8serno < 4)
799  u8current = port->available();
800  else
801  u8current = softPort->available();
802 
803  if (u8current == 0)
804  return 0;
805 
806  // check T35 after frame end or still no frame end
807  if (u8current != u8lastRec)
808  {
809  u8lastRec = u8current;
810  u32time = millis() + T35;
811  return 0;
812  }
813  if (millis() < u32time)
814  return 0;
815 
816  u8lastRec = 0;
817  int8_t i8state = getRxBuffer();
818  u8lastError = i8state;
819  if (i8state < 7)
820  return i8state;
821 
822  // check slave id
823  if (au8Buffer[ID] != u8id)
824  {
825  return 0;
826  }
827 
828  // validate message: CRC, FCT, address and size
829  uint8_t u8exception = validateRequest();
830  if (u8exception > 0)
831  {
832  Serial.println("exception : 1");
833  if (u8exception != NO_REPLY)
834  {
835  buildException(u8exception);
836  sendTxBuffer();
837  }
838  u8lastError = u8exception;
839  return u8exception;
840  }
841 
842  u32timeOut = millis() + long(u16timeOut);
843  u8lastError = 0;
844 
845  Log.verboseln("mb : process message : %d", FUNC);
846 
847  // process message
848  switch (au8Buffer[FUNC])
849  {
850  case MB_FC_READ_COILS:
851  case MB_FC_READ_DISCRETE_INPUT:
852  return process_FC1(regs, u8size);
853  break;
854  case MB_FC_READ_INPUT_REGISTER:
855  case MB_FC_READ_REGISTERS:
856  return process_FC3(regs, u8size);
857  break;
858  case MB_FC_WRITE_COIL:
859  return process_FC5(regs, u8size);
860  break;
861  case MB_FC_WRITE_REGISTER:
862  return process_FC6(regs, u8size);
863  break;
864  case MB_FC_WRITE_MULTIPLE_COILS:
865  return process_FC15(regs, u8size);
866  break;
867  case MB_FC_WRITE_MULTIPLE_REGISTERS:
868  return process_FC16(regs, u8size);
869  break;
870  default:
871  break;
872  }
874  return i8state;
875 }
876 
877 /* _____PRIVATE FUNCTIONS_____________________________________________________ */
878 
879 void Modbus::init(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin)
880 {
881  this->u8id = u8id;
882  this->u8serno = (u8serno > 3) ? 0 : u8serno;
883 
884  // this->u8txenpin = u8txenpin;
885  this->u8txenpin = 2; // Ignore input parameter u8txenpin and hardcode 2 for compatibility
886 
887  this->u16timeOut = 1000;
888 }
889 
890 void Modbus::init(uint8_t u8id)
891 {
892  this->u8id = u8id;
893  this->u8serno = 4;
894  this->u8txenpin = 0;
895  this->u16timeOut = 1000;
896 }
897 
906 {
907  boolean bBuffOverflow = false;
908 
909  if (u8txenpin > 1)
910  {
913  // digitalWrite( u8txenpin, LOW );
914  }
915 
916  u8BufferSize = 0;
917  if (u8serno < 4)
918  while (port->available())
919  {
920  au8Buffer[u8BufferSize] = port->read();
922  u8BufferSize++;
923 
924  if (u8BufferSize >= MAX_BUFFER)
925  bBuffOverflow = true;
926  }
927  else
928  while (softPort->available())
929  {
930  au8Buffer[u8BufferSize] = softPort->read();
931  u8BufferSize++;
932 
933  if (u8BufferSize >= MAX_BUFFER)
934  bBuffOverflow = true;
935  }
936  u16InCnt++;
937 
938  if (bBuffOverflow)
939  {
940  Log.verboseln("buffer overflow: %d", u8BufferSize);
941  u16errCnt++;
942  return ERR_BUFF_OVERFLOW;
943  }
944  //Log.verboseln("getRX-Buffer : %d",u8BufferSize);
945  //printHex(rxBuffer, u8BufferSize);
947  return u8BufferSize;
948 }
949 
963 {
964  uint8_t i = 0;
965 
966  // append CRC to message
967  uint16_t u16crc = calcCRC(u8BufferSize);
968 /*
969  Serial.print("---- calc CRC : HEX : ");
970  Serial.print(u16crc, DEC);
971  Serial.print(" - HEX : ");
972  Serial.print(u16crc, HEX);
973  Serial.print(" - ");
974  Serial.print(u8BufferSize);
975  Serial.print("\n");
976  */
977 
978  au8Buffer[u8BufferSize] = u16crc >> 8;
979  u8BufferSize++;
980  au8Buffer[u8BufferSize] = u16crc & 0x00ff;
981  u8BufferSize++;
982 
983  if (debugSend)
984  {
985  Serial.print("Send Hex : \t");
986  for (int i = 0; i < 8; i++)
987  {
988  Serial.print(au8Buffer[i], HEX);
989  Serial.print(" : ");
990  }
991  Serial.println("--------------- \n");
992  }
993 
994  // set RS485 transceiver to transmit mode
995  if (u8txenpin > 1)
996  {
997  switch (u8serno)
998  {
999 #if defined(UBRR1H)
1000  case 1:
1001  UCSR1A = UCSR1A | (1 << TXC1);
1002  break;
1003 #endif
1004 
1005 #if defined(UBRR2H)
1006  case 2:
1007  UCSR2A = UCSR2A | (1 << TXC2);
1008  break;
1009 #endif
1010 
1011 #if defined(UBRR3H)
1012  case 3:
1013  UCSR3A = UCSR3A | (1 << TXC3);
1014  break;
1015 #endif
1016  case 0:
1017  default:
1018  UCSR0A = UCSR0A | (1 << TXC0);
1019  break;
1020  }
1021  RS485_SET_DE;
1022  RS485_SET_RE;
1023  // digitalWrite( u8txenpin, HIGH );
1024  }
1025 
1026  // transfer buffer to serial line
1027  if (u8serno < 4)
1028  {
1029  port->write(au8Buffer, u8BufferSize);
1030  }
1031  else
1032  {
1033  softPort->write(au8Buffer, u8BufferSize);
1034  }
1035  // keep RS485 transceiver in transmit mode as long as sending
1036  if (u8txenpin > 1)
1037  {
1038  switch (u8serno)
1039  {
1040 #if defined(UBRR1H)
1041  case 1:
1042  while (!(UCSR1A & (1 << TXC1)))
1043  ;
1044  break;
1045 #endif
1046 
1047 #if defined(UBRR2H)
1048  case 2:
1049  while (!(UCSR2A & (1 << TXC2)))
1050  ;
1051  break;
1052 #endif
1053 
1054 #if defined(UBRR3H)
1055  case 3:
1056  while (!(UCSR3A & (1 << TXC3)))
1057  ;
1058  break;
1059 #endif
1060  case 0:
1061  default:
1062  while (!(UCSR0A & (1 << TXC0)))
1063  ;
1064  break;
1065  }
1066  // return RS485 transceiver to receive mode
1069  // digitalWrite( u8txenpin, LOW );
1070  }
1071  if (u8serno < 4)
1072  while (port->read() >= 0)
1073  ;
1074  else
1075  while (softPort->read() >= 0)
1076  ;
1077 
1078  u8BufferSize = 0;
1079 
1080  // set time-out for master
1081  u32timeOut = millis() + (unsigned long)u16timeOut;
1082 
1083  // increase message counter
1084  u16OutCnt++;
1085 
1087 }
1088 
1096 uint16_t Modbus::calcCRC(uint8_t u8length)
1097 {
1098  unsigned int temp, temp2, flag;
1099  temp = 0xFFFF;
1100  for (unsigned char i = 0; i < u8length; i++)
1101  {
1102  temp = temp ^ au8Buffer[i];
1103  // Serial.println(au8Buffer[i], DEC);
1104  for (unsigned char j = 1; j <= 8; j++)
1105  {
1106  flag = temp & 0x0001;
1107  temp >>= 1;
1108  if (flag)
1109  temp ^= 0xA001;
1110  }
1111  }
1112  // Reverse byte order.
1113  temp2 = temp >> 8;
1114  temp = (temp << 8) | temp2;
1115  temp &= 0xFFFF;
1116  // the returned value is already swapped
1117  // crcLo byte is first & crcHi byte is last
1118  return temp;
1119 }
1120 
1129 {
1130  // check message crc vs calculated crc
1131  uint16_t u16MsgCRC =
1132  ((au8Buffer[u8BufferSize - 2] << 8) | au8Buffer[u8BufferSize - 1]); // combine the crc Low & High bytes
1133  if (calcCRC(u8BufferSize - 2) != u16MsgCRC)
1134  {
1135  u16errCnt++;
1136  return NO_REPLY;
1137  }
1138 
1139  // check fct code
1140  boolean isSupported = false;
1141  for (uint8_t i = 0; i < sizeof(fctsupported); i++)
1142  {
1143  if (fctsupported[i] == au8Buffer[FUNC])
1144  {
1145  isSupported = 1;
1146  break;
1147  }
1148  }
1149  if (!isSupported)
1150  {
1151  u16errCnt++;
1152  return EXC_FUNC_CODE;
1153  }
1154 
1155  // check start address & nb range
1156  uint16_t u16regs = 0;
1157  uint8_t u8regs;
1158  switch (au8Buffer[FUNC])
1159  {
1160  case MB_FC_READ_COILS:
1161  case MB_FC_READ_DISCRETE_INPUT:
1162  case MB_FC_WRITE_MULTIPLE_COILS:
1163  u16regs = word(au8Buffer[ADD_HI], au8Buffer[ADD_LO]) / 16;
1164  u16regs += word(au8Buffer[NB_HI], au8Buffer[NB_LO]) / 16;
1165  u8regs = (uint8_t)u16regs;
1166  if (u8regs > u8regsize)
1167  return EXC_ADDR_RANGE;
1168  break;
1169  case MB_FC_WRITE_COIL:
1170  u16regs = word(au8Buffer[ADD_HI], au8Buffer[ADD_LO]) / 16;
1171  u8regs = (uint8_t)u16regs;
1172  if (u8regs > u8regsize)
1173  return EXC_ADDR_RANGE;
1174  break;
1175  case MB_FC_WRITE_REGISTER:
1176  u16regs = word(au8Buffer[ADD_HI], au8Buffer[ADD_LO]);
1177  u8regs = (uint8_t)u16regs;
1178  if (u8regs > u8regsize)
1179  return EXC_ADDR_RANGE;
1180  break;
1181  case MB_FC_READ_REGISTERS:
1182  case MB_FC_READ_INPUT_REGISTER:
1183  case MB_FC_WRITE_MULTIPLE_REGISTERS:
1184  u16regs = word(au8Buffer[ADD_HI], au8Buffer[ADD_LO]);
1185  u16regs += word(au8Buffer[NB_HI], au8Buffer[NB_LO]);
1186  u8regs = (uint8_t)u16regs;
1187  if (u8regs > u8regsize)
1188  return EXC_ADDR_RANGE;
1189  break;
1190  }
1191  return 0; // OK, no exception code thrown
1192 }
1193 
1202 {
1203  // check message crc vs calculated crc
1204  uint16_t u16MsgCRC =
1205  ((au8Buffer[u8BufferSize - 2] << 8) | au8Buffer[u8BufferSize - 1]); // combine the crc Low & High bytes
1206  if (calcCRC(u8BufferSize - 2) != u16MsgCRC)
1207  {
1208  u16errCnt++;
1209  return NO_REPLY;
1210  }
1211 
1212  // check exception
1213  if ((au8Buffer[FUNC] & 0x80) != 0)
1214  {
1215  u16errCnt++;
1216  return ERR_EXCEPTION;
1217  }
1218 
1219  // check fct code
1220  boolean isSupported = false;
1221  for (uint8_t i = 0; i < sizeof(fctsupported); i++)
1222  {
1223  if (fctsupported[i] == au8Buffer[FUNC])
1224  {
1225  isSupported = 1;
1226  break;
1227  }
1228  }
1229  if (!isSupported)
1230  {
1231  u16errCnt++;
1232  return EXC_FUNC_CODE;
1233  }
1234 
1235  return 0; // OK, no exception code thrown
1236 }
1237 
1244 void Modbus::buildException(uint8_t u8exception)
1245 {
1246  uint8_t u8func = au8Buffer[FUNC]; // get the original FUNC code
1247 
1248  au8Buffer[ID] = u8id;
1249  au8Buffer[FUNC] = u8func + 0x80;
1250  au8Buffer[2] = u8exception;
1252 }
1253 
1262 {
1263  uint8_t u8byte, i;
1264  u8byte = 0;
1265 
1266  // for (i=0; i< au8Buffer[ 2 ] /2; i++) {
1267  // au16regs[ i ] = word(
1268  // au8Buffer[ u8byte ],
1269  // au8Buffer[ u8byte +1 ]);
1270  // u8byte += 2;
1271  // }
1272 }
1273 
1281 {
1282  uint8_t u8byte, i;
1283  u8byte = 3;
1284 
1285  for (i = 0; i < au8Buffer[2] / 2; i++)
1286  {
1287  au16regs[i] = word(
1288  au8Buffer[u8byte],
1289  au8Buffer[u8byte + 1]);
1290  u8byte += 2;
1291  }
1292 }
1293 
1302 int8_t Modbus::process_FC1(uint16_t *regs, uint8_t u8size)
1303 {
1304  uint8_t u8currentRegister, u8currentBit, u8bytesno, u8bitsno;
1305  uint8_t u8CopyBufferSize;
1306  uint16_t u16currentCoil, u16coil;
1307 
1308  // get the first and last coil from the message
1309  uint16_t u16StartCoil = word(au8Buffer[ADD_HI], au8Buffer[ADD_LO]);
1310  uint16_t u16Coilno = word(au8Buffer[NB_HI], au8Buffer[NB_LO]);
1311 
1312  // put the number of bytes in the outcoming message
1313  u8bytesno = (uint8_t)(u16Coilno / 8);
1314  if (u16Coilno % 8 != 0)
1315  u8bytesno++;
1316  au8Buffer[ADD_HI] = u8bytesno;
1317  u8BufferSize = ADD_LO;
1318 
1319  // read each coil from the register map and put its value inside the outcoming message
1320  u8bitsno = 0;
1321 
1322  for (u16currentCoil = 0; u16currentCoil < u16Coilno; u16currentCoil++)
1323  {
1324  u16coil = u16StartCoil + u16currentCoil;
1325  u8currentRegister = (uint8_t)(u16coil / 16);
1326  u8currentBit = (uint8_t)(u16coil % 16);
1327 
1328  bitWrite(
1330  u8bitsno,
1331  bitRead(regs[u8currentRegister], u8currentBit));
1332  u8bitsno++;
1333 
1334  if (u8bitsno > 7)
1335  {
1336  u8bitsno = 0;
1337  u8BufferSize++;
1338  }
1339  }
1340 
1341  // send outcoming message
1342  if (u16Coilno % 8 != 0)
1343  u8BufferSize++;
1344  u8CopyBufferSize = u8BufferSize + 2;
1345  sendTxBuffer();
1346  return u8CopyBufferSize;
1347 }
1348 
1357 int8_t Modbus::process_FC3(uint16_t *regs, uint8_t u8size)
1358 {
1359 
1360  uint8_t u8StartAdd = word(au8Buffer[ADD_HI], au8Buffer[ADD_LO]);
1361  uint8_t u8regsno = word(au8Buffer[NB_HI], au8Buffer[NB_LO]);
1362  uint8_t u8CopyBufferSize;
1363  uint8_t i;
1364 
1365  au8Buffer[2] = u8regsno * 2;
1366  u8BufferSize = 3;
1367 
1368  for (i = u8StartAdd; i < u8StartAdd + u8regsno; i++)
1369  {
1370  au8Buffer[u8BufferSize] = highByte(regs[i]);
1371  u8BufferSize++;
1372  au8Buffer[u8BufferSize] = lowByte(regs[i]);
1373  u8BufferSize++;
1374  }
1375  u8CopyBufferSize = u8BufferSize + 2;
1376  sendTxBuffer();
1377 
1378  return u8CopyBufferSize;
1379 }
1380 
1389 int8_t Modbus::process_FC5(uint16_t *regs, uint8_t u8size)
1390 {
1391  uint8_t u8currentRegister, u8currentBit;
1392  uint8_t u8CopyBufferSize;
1393  uint16_t u16coil = word(au8Buffer[ADD_HI], au8Buffer[ADD_LO]);
1394 
1395  // point to the register and its bit
1396  u8currentRegister = (uint8_t)(u16coil / 16);
1397  u8currentBit = (uint8_t)(u16coil % 16);
1398 
1399  // write to coil
1400  bitWrite(
1401  regs[u8currentRegister],
1402  u8currentBit,
1403  au8Buffer[NB_HI] == 0xff);
1404 
1405  // send answer to master
1406  u8BufferSize = 6;
1407  u8CopyBufferSize = u8BufferSize + 2;
1408  sendTxBuffer();
1409 
1410  return u8CopyBufferSize;
1411 }
1412 
1421 int8_t Modbus::process_FC6(uint16_t *regs, uint8_t u8size)
1422 {
1423 
1424  uint8_t u8add = word(au8Buffer[ADD_HI], au8Buffer[ADD_LO]);
1425  uint8_t u8CopyBufferSize;
1426  uint16_t u16val = word(au8Buffer[NB_HI], au8Buffer[NB_LO]);
1427 
1428  regs[u8add] = u16val;
1429 
1430  // keep the same header
1432 
1433  u8CopyBufferSize = u8BufferSize + 2;
1434  sendTxBuffer();
1435 
1436  return u8CopyBufferSize;
1437 }
1438 
1447 int8_t Modbus::process_FC15(uint16_t *regs, uint8_t u8size)
1448 {
1449  uint8_t u8currentRegister, u8currentBit, u8frameByte, u8bitsno;
1450  uint8_t u8CopyBufferSize;
1451  uint16_t u16currentCoil, u16coil;
1452  boolean bTemp;
1453 
1454  // get the first and last coil from the message
1455  uint16_t u16StartCoil = word(au8Buffer[ADD_HI], au8Buffer[ADD_LO]);
1456  uint16_t u16Coilno = word(au8Buffer[NB_HI], au8Buffer[NB_LO]);
1457 
1458  // read each coil from the register map and put its value inside the outcoming message
1459  u8bitsno = 0;
1460  u8frameByte = 7;
1461  for (u16currentCoil = 0; u16currentCoil < u16Coilno; u16currentCoil++)
1462  {
1463 
1464  u16coil = u16StartCoil + u16currentCoil;
1465  u8currentRegister = (uint8_t)(u16coil / 16);
1466  u8currentBit = (uint8_t)(u16coil % 16);
1467 
1468  bTemp = bitRead(
1469  au8Buffer[u8frameByte],
1470  u8bitsno);
1471 
1472  bitWrite(
1473  regs[u8currentRegister],
1474  u8currentBit,
1475  bTemp);
1476 
1477  u8bitsno++;
1478 
1479  if (u8bitsno > 7)
1480  {
1481  u8bitsno = 0;
1482  u8frameByte++;
1483  }
1484  }
1485 
1486  // send outcoming message
1487  // it's just a copy of the incomping frame until 6th byte
1488  u8BufferSize = 6;
1489  u8CopyBufferSize = u8BufferSize + 2;
1490  sendTxBuffer();
1491  return u8CopyBufferSize;
1492 }
1493 
1502 int8_t Modbus::process_FC16(uint16_t *regs, uint8_t u8size)
1503 {
1504  uint8_t u8func = au8Buffer[FUNC]; // get the original FUNC code
1505  uint8_t u8StartAdd = au8Buffer[ADD_HI] << 8 | au8Buffer[ADD_LO];
1506  uint8_t u8regsno = au8Buffer[NB_HI] << 8 | au8Buffer[NB_LO];
1507  uint8_t u8CopyBufferSize;
1508  uint8_t i;
1509  uint16_t temp;
1510 
1511  // build header
1512  au8Buffer[NB_HI] = 0;
1513  au8Buffer[NB_LO] = u8regsno;
1515 
1516  // write registers
1517  for (i = 0; i < u8regsno; i++)
1518  {
1519  temp = word(
1520  au8Buffer[(BYTE_CNT + 1) + i * 2],
1521  au8Buffer[(BYTE_CNT + 2) + i * 2]);
1522 
1523  regs[u8StartAdd + i] = temp;
1524  }
1525  u8CopyBufferSize = u8BufferSize + 2;
1526  sendTxBuffer();
1527 
1528  return u8CopyBufferSize;
1529 }
EXC_ADDR_RANGE
@ EXC_ADDR_RANGE
Definition: ModbusRtu.h:130
MAX_BUFFER
#define MAX_BUFFER
maximum size for the communication buffer in bytes
Definition: ModbusRtu.h:57
RS485_CLEAR_DE
#define RS485_CLEAR_DE
Definition: ModbusRtu.h:51
Modbus::getTimeOutState
boolean getTimeOutState()
get communication watch-dog timer state
Definition: ModbusRtu.h:506
Modbus::setTimeOut
void setTimeOut(uint16_t u16timeout)
write communication watch-dog timer
Definition: ModbusRtu.h:493
EXC_REGS_QUANT
@ EXC_REGS_QUANT
Definition: ModbusRtu.h:131
Modbus::u32time
uint32_t u32time
Definition: ModbusRtu.h:168
Modbus::validateAnswer
uint8_t validateAnswer()
This method validates master incoming messages.
Definition: ModbusRtu.h:1201
Modbus::u32timeOut
uint32_t u32timeOut
Definition: ModbusRtu.h:168
Modbus::get_FC1
void get_FC1()
Definition: ModbusRtu.h:1261
ERR_LIST
ERR_LIST
Definition: ModbusRtu.h:117
COM_IDLE
@ COM_IDLE
Definition: ModbusRtu.h:113
Modbus::getErrCnt
uint16_t getErrCnt()
error counter
Definition: ModbusRtu.h:545
RS485_PIN_DE
#define RS485_PIN_DE
Definition: ModbusRtu.h:47
Modbus::u8BufferSize
uint8_t u8BufferSize
Definition: ModbusRtu.h:163
Modbus::debugSend
bool debugSend
Definition: ModbusRtu.h:214
modbus_t::flags
uint8_t flags
Definition: ModbusRtu.h:74
modbus_t::au16reg
uint16_t * au16reg
Definition: ModbusRtu.h:73
Modbus::u8state
uint8_t u8state
Definition: ModbusRtu.h:160
fctsupported
const unsigned char fctsupported[]
Definition: ModbusRtu.h:135
Modbus::buildException
void buildException(uint8_t u8exception)
This method builds an exception message.
Definition: ModbusRtu.h:1244
Modbus::get_FC3
void get_FC3()
Definition: ModbusRtu.h:1280
Modbus::getOutCnt
uint16_t getOutCnt()
number of outcoming messages
Definition: ModbusRtu.h:532
Modbus::getTimeOut
uint16_t getTimeOut()
get communication watch-dog timer value
Modbus::u8lastRec
uint8_t u8lastRec
Definition: ModbusRtu.h:164
modbus_t
Master query structure: This includes all the necessary fields to make the Master generate a Modbus q...
Definition: ModbusRtu.h:67
RS485_SET_DE
#define RS485_SET_DE
Definition: ModbusRtu.h:53
Modbus::currentQuery
modbus_t * currentQuery
Definition: ModbusRtu.h:178
EXC_FUNC_CODE
@ EXC_FUNC_CODE
Definition: ModbusRtu.h:129
Modbus::getState
uint8_t getState()
Definition: ModbusRtu.h:561
NB_HI
@ NB_HI
Number of coils or registers high byte.
Definition: ModbusRtu.h:97
modbus_t::coilValue
bool coilValue
Definition: ModbusRtu.h:75
Modbus::u8regsize
uint8_t u8regsize
Definition: ModbusRtu.h:169
ERR_BAD_CRC
@ ERR_BAD_CRC
Definition: ModbusRtu.h:122
Modbus::process_FC15
int8_t process_FC15(uint16_t *regs, uint8_t u8size)
This method processes function 15 This method writes a bit array assigned by the master.
Definition: ModbusRtu.h:1447
Modbus::au16regs
uint16_t * au16regs
Definition: ModbusRtu.h:165
Modbus::u16errCnt
uint16_t u16errCnt
Definition: ModbusRtu.h:166
Modbus::process_FC5
int8_t process_FC5(uint16_t *regs, uint8_t u8size)
This method processes function 5 This method writes a value assigned by the master to a single bit.
Definition: ModbusRtu.h:1389
RS485_PIN_RE
#define RS485_PIN_RE
Definition: ModbusRtu.h:48
Modbus::validateRequest
uint8_t validateRequest()
This method validates slave incoming messages.
Definition: ModbusRtu.h:1128
utils.h
Modbus::u8serno
uint8_t u8serno
serial port: 0-Serial, 1..3-Serial1..Serial3; 4: use software serial
Definition: ModbusRtu.h:158
COM_WAITING
@ COM_WAITING
Definition: ModbusRtu.h:114
printHex
void printHex(uint8_t *data, uint8_t length)
Definition: utils.cpp:3
ERR_NOT_MASTER
@ ERR_NOT_MASTER
Definition: ModbusRtu.h:119
Modbus::query
int8_t query(modbus_t telegram)
only for master
Definition: ModbusRtu.h:592
CHECKSUM_SIZE
@ CHECKSUM_SIZE
Definition: ModbusRtu.h:83
Modbus::u16OutCnt
uint16_t u16OutCnt
Definition: ModbusRtu.h:166
Modbus::init
void init(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin)
Definition: ModbusRtu.h:879
ku8MBLinkTestOmronMX2Only
#define ku8MBLinkTestOmronMX2Only
Modbus function 0x08 Test.
Definition: enums.h:109
Modbus::calcCRC
uint16_t calcCRC(uint8_t u8length)
This method calculates CRC.
Definition: ModbusRtu.h:1096
Modbus::rxSize
uint8_t rxSize
Definition: ModbusRtu.h:216
ERR_BUFF_OVERFLOW
@ ERR_BUFF_OVERFLOW
Definition: ModbusRtu.h:121
Modbus::u16InCnt
uint16_t u16InCnt
Definition: ModbusRtu.h:166
Modbus::getID
uint8_t getID()
get slave ID between 1 and 247
Definition: ModbusRtu.h:477
MESSAGE
MESSAGE
Indexes to telegram frame positions.
Definition: ModbusRtu.h:91
Modbus::setID
void setID(uint8_t u8id)
write new ID for the slave
Definition: ModbusRtu.h:462
Modbus::rxBuffer
uint8_t rxBuffer[MAX_BUFFER]
Definition: ModbusRtu.h:217
modbus_t::u16CoilsNo
uint16_t u16CoilsNo
Definition: ModbusRtu.h:72
RS485_CLEAR_RE
#define RS485_CLEAR_RE
Definition: ModbusRtu.h:52
Modbus::end
void end()
finish any communication and release serial communication port
Modbus::sendTxBuffer
void sendTxBuffer()
This method transmits au8Buffer to Serial line. Only if u8txenpin != 0, there is a flow handling in o...
Definition: ModbusRtu.h:962
Modbus::process_FC6
int8_t process_FC6(uint16_t *regs, uint8_t u8size)
This method processes function 6 This method writes a value assigned by the master to a single word.
Definition: ModbusRtu.h:1421
EXC_EXECUTE
@ EXC_EXECUTE
Definition: ModbusRtu.h:132
EXCEPTION_SIZE
@ EXCEPTION_SIZE
Definition: ModbusRtu.h:82
modbus_t::u8id
uint8_t u8id
Definition: ModbusRtu.h:69
modbus_t::u8fct
uint8_t u8fct
Definition: ModbusRtu.h:70
Modbus::clearError
void clearError()
Definition: ModbusRtu.h:550
Modbus::u16timeOut
uint16_t u16timeOut
Definition: ModbusRtu.h:167
ERR_EXCEPTION
@ ERR_EXCEPTION
Definition: ModbusRtu.h:123
RESPONSE_SIZE
@ RESPONSE_SIZE
Definition: ModbusRtu.h:81
QUEUED
@ QUEUED
Definition: enums.h:90
Modbus::u8txenpin
uint8_t u8txenpin
flow control pin: 0=USB or RS-232 mode, >0=RS-485 mode
Definition: ModbusRtu.h:159
Modbus::isLocked
bool isLocked()
Definition: ModbusRtu.h:344
COM_STATES
COM_STATES
Definition: ModbusRtu.h:111
Modbus::au8Buffer
uint8_t au8Buffer[MAX_BUFFER]
Definition: ModbusRtu.h:162
RS485_DIR_REG
#define RS485_DIR_REG
Definition: ModbusRtu.h:49
modbus_t::u16RegAdd
uint16_t u16RegAdd
Definition: ModbusRtu.h:71
FUNC
@ FUNC
Function code position.
Definition: ModbusRtu.h:94
Modbus::Modbus
Modbus()
Default Constructor for Master through Serial.
Definition: ModbusRtu.h:228
Modbus::process_FC1
int8_t process_FC1(uint16_t *regs, uint8_t u8size)
This method processes functions 1 & 2 This method reads a bit array and transfers it to the master.
Definition: ModbusRtu.h:1302
Modbus::getInCnt
uint16_t getInCnt()
number of incoming messages
Definition: ModbusRtu.h:519
enums.h
NO_REPLY
@ NO_REPLY
Definition: ModbusRtu.h:128
ADD_HI
@ ADD_HI
Address high byte.
Definition: ModbusRtu.h:95
SENT
@ SENT
Definition: enums.h:92
Modbus::u8lastError
uint8_t u8lastError
Definition: ModbusRtu.h:161
RS485_SET_RE
#define RS485_SET_RE
Definition: ModbusRtu.h:54
Modbus::process_FC16
int8_t process_FC16(uint16_t *regs, uint8_t u8size)
This method processes function 16 This method writes a word array assigned by the master.
Definition: ModbusRtu.h:1502
RESPONDED
@ RESPONDED
Definition: enums.h:93
Modbus::process_FC3
int8_t process_FC3(uint16_t *regs, uint8_t u8size)
This method processes functions 3 & 4 This method reads a word array and transfers it to the master.
Definition: ModbusRtu.h:1357
Modbus::getLastError
uint8_t getLastError()
get last error message
Definition: ModbusRtu.h:575
Modbus::begin
void begin()
Definition: ModbusRtu.h:450
Modbus::softPort
SoftwareSerial * softPort
Pointer to SoftwareSerial class object.
Definition: ModbusRtu.h:156
Modbus::port
HardwareSerial * port
Pointer to Serial class object.
Definition: ModbusRtu.h:155
ID
@ ID
ID field.
Definition: ModbusRtu.h:93
ERR_POLLING
@ ERR_POLLING
Definition: ModbusRtu.h:120
BYTE_CNT
@ BYTE_CNT
byte counter
Definition: ModbusRtu.h:99
Modbus::u8id
uint8_t u8id
0=master, 1..247=slave number
Definition: ModbusRtu.h:157
T35
#define T35
Definition: ModbusRtu.h:56
Modbus::getRxBuffer
int8_t getRxBuffer()
This method moves Serial buffer data to the Modbus au8Buffer.
Definition: ModbusRtu.h:905
ADD_LO
@ ADD_LO
Address low byte.
Definition: ModbusRtu.h:96
Modbus::poll
int8_t poll()
cyclic poll for master
Definition: ModbusRtu.h:691
NB_LO
@ NB_LO
Number of coils or registers low byte.
Definition: ModbusRtu.h:98
Modbus
Arduino class library for communicating with Modbus devices over USB/RS232/485 (via RTU protocol).
Definition: ModbusRtu.h:152