API
modbus.hpp
Go to the documentation of this file.
1 
2 // header only version of modbuspp
3 //
4 //
5 // Moduspp was created by Fanzhe on 5/28/2017.
6 //
7 // MagAO-X:
8 // Cloned from https://github.com/fanzhe98/modbuspp
9 // On commit 73cabdc, Date: Sat Nov 24 23:16:51 2018 -0500
10 //
11 // License: GPLv3
12 //
13 // Header only version created by Jared Males
14 // -- Added to all class function definitions, and removed "using namespace std".
15 // -- commented out all cout diagnostics
16 //
17 
18 #ifndef MODBUSPP_MODBUS_H
19 #define MODBUSPP_MODBUS_H
20 
21 #include <string>
22 
23 #include <iostream>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <unistd.h>
28 #include "modbus_exception.hpp"
29 
30 
31 #define MAX_MSG_LENGTH 260
32 
33 ///Function Code
34 enum{
35  READ_COILS = 0x01,
37  READ_REGS = 0x03,
39  WRITE_COIL = 0x05,
40  WRITE_REG = 0x06,
41  WRITE_COILS = 0x0F,
42  WRITE_REGS = 0x10,
43 };
44 
45 ///Exception Codes
46 enum {
47  EX_ILLEGAL_FUNCTION = 0x01, // Function Code not Supported
48  EX_ILLEGAL_ADDRESS = 0x02, // Output Address not exists
49  EX_ILLEGAL_VALUE = 0x03, // Output Value not in Range
50  EX_SERVER_FAILURE = 0x04, // Slave Deive Fails to process request
51  EX_ACKNOWLEDGE = 0x05, // Service Need Long Time to Execute
52  EX_SERVER_BUSY = 0x06, // Server Was Unable to Accept MB Request PDU
53  EX_GATEWAY_PROBLEMP = 0x0A, // Gateway Path not Available
54  EX_GATEWYA_PROBLEMF = 0x0B, // Target Device Failed to Response
55 };
56 
57 
58 /// Modbus Operator Class
59 /**
60  * Modbus Operator Class
61  * Providing networking support and mobus operation support.
62  */
63 class modbus {
64 private:
65  bool _connected {false};
66  uint16_t PORT {502};
67  int _socket {0};
68  int _msg_id {1};
69  int _slaveid {1};
70  std::string HOST;
71  struct sockaddr_in _server;
72 
73  void modbus_build_request(uint8_t *to_send,int address, int func);
74 
75  void modbus_read(int address, int amount, int func);
76  void modbus_write(int address, int amount, int func, uint16_t *value);
77 
78  ssize_t modbus_send(uint8_t *to_send, int length);
79  ssize_t modbus_receive(uint8_t *buffer);
80 
81  void modbus_error_handle(uint8_t *msg, int func);
82 
83 
84 public:
85  modbus(const std::string & host, uint16_t port);
86  explicit modbus(const std::string & host);
87  ~modbus();
88 
89  bool modbus_connect();
90  void modbus_close();
91 
92  void modbus_set_slave_id(int id);
93 
94  void modbus_read_coils(int address, int amount, bool* buffer);
95  void modbus_read_input_bits(int address, int amount, bool* buffer);
96  void modbus_read_holding_registers(int address, int amount, uint16_t *buffer);
97  void modbus_read_input_registers(int address, int amount, uint16_t *buffer);
98 
99  void modbus_write_coil(int address, bool to_write);
100  void modbus_write_register(int address, uint16_t value);
101  void modbus_write_coils(int address, int amount, bool* value );
102  void modbus_write_registers(int address, int amount, uint16_t *value);
103 };
104 
105 
106 #if 0
107 //-- leaving in for comments until can be normalized.
108 
109 /**
110  * Main Constructor of Modbus Connector Object
111  * @param host IP Address of Host
112  * @param port Port for the TCP Connection
113  * @return A Modbus Connector Object
114  */
115 
116 modbus::modbus( const std::string & host,
117  uint16_t port
118  ) : PORT {port}, HOST{host} {}
119 
120 
121 /**
122  * Overloading Modbus Connector Constructor with Default Port Set at 502
123  * @param host IP Address of Host
124  * @return A Modbus Connector Object
125  */
126 
127 modbus::modbus(const std::string & host) : HOST{host} {}
128 
129 
130 /**
131  * Destructor of Modbus Connector Object
132  */
133 
134 modbus::~modbus(void) {
135 }
136 
137 
138 /**
139  * Modbus Slave ID Setter
140  * @param id ID of the Modbus Server Slave
141  */
142 
143 void modbus::modbus_set_slave_id(int id) {
144  _slaveid = id;
145 }
146 
147 
148 
149 /**
150  * Build up a Modbus/TCP Connection
151  * @return If A Connection Is Successfully Built
152  */
153 
154 bool modbus::modbus_connect() {
155  if(HOST == "" || PORT == 0) {
156  //std::cout << "Missing Host and Port" << std::endl;
157  return false;
158  } else {
159  //std::cout << "Found Proper Host "<< HOST << " and Port " <<PORT <<std::endl;
160  }
161 
162  _socket = socket(AF_INET, SOCK_STREAM, 0);
163  if(_socket == -1) {
164  //std::cout <<"Error Opening Socket" <<std::endl;
165  return false;
166  } else {
167  //std::cout <<"Socket Opened Successfully" << std::endl;
168  }
169 
170  _server.sin_family = AF_INET;
171  _server.sin_addr.s_addr = inet_addr(HOST.c_str());
172  _server.sin_port = htons(PORT);
173 
174  if (connect(_socket, (struct sockaddr*)&_server, sizeof(_server)) < 0) {
175  //std::cout<< "Connection Error" << std::endl;
176  return false;
177  }
178 
179  //std::cout<< "Connected" <<std::endl;
180  _connected = true;
181  return true;
182 }
183 
184 
185 /**
186  * Close the Modbus/TCP Connection
187  */
188 
189 void modbus::modbus_close() {
190  close(_socket);
191  //std::cout <<"Socket Closed" <<std::endl;
192 }
193 
194 
195 /**
196  * Modbus Request Builder
197  * @param to_send Message Buffer to Be Sent
198  * @param address Reference Address
199  * @param func Modbus Functional Code
200  */
201 
202 void modbus::modbus_build_request(uint8_t *to_send, int address, int func) {
203  to_send[0] = (uint8_t) _msg_id >> 8;
204  to_send[1] = (uint8_t) (_msg_id & 0x00FF);
205  to_send[2] = 0;
206  to_send[3] = 0;
207  to_send[4] = 0;
208  to_send[6] = (uint8_t) _slaveid;
209  to_send[7] = (uint8_t) func;
210  to_send[8] = (uint8_t) (address >> 8);
211  to_send[9] = (uint8_t) (address & 0x00FF);
212 }
213 
214 
215 /**
216  * Write Request Builder and Sender
217  * @param address Reference Address
218  * @param amount Amount of data to be Written
219  * @param func Modbus Functional Code
220  * @param value Data to Be Written
221  */
222 
223 void modbus::modbus_write(int address, int amount, int func, uint16_t *value) {
224  if(func == WRITE_COIL || func == WRITE_REG) {
225  uint8_t to_send[12];
226  modbus_build_request(to_send, address, func);
227  to_send[5] = 6;
228  to_send[10] = (uint8_t) (value[0] >> 8);
229  to_send[11] = (uint8_t) (value[0] & 0x00FF);
230  modbus_send(to_send, 12);
231  } else if(func == WRITE_REGS){
232  uint8_t to_send[13 + 2 * amount];
233  modbus_build_request(to_send, address, func);
234  to_send[5] = (uint8_t) (5 + 2 * amount);
235  to_send[10] = (uint8_t) (amount >> 8);
236  to_send[11] = (uint8_t) (amount & 0x00FF);
237  to_send[12] = (uint8_t) (2 * amount);
238  for(int i = 0; i < amount; i++) {
239  to_send[13 + 2 * i] = (uint8_t) (value[i] >> 8);
240  to_send[14 + 2 * i] = (uint8_t) (value[i] & 0x00FF);
241  }
242  modbus_send(to_send, 13 + 2 * amount);
243  } else if(func == WRITE_COILS) {
244  uint8_t to_send[14 + (amount -1) / 8 ];
245  modbus_build_request(to_send, address, func);
246  to_send[5] = (uint8_t) (7 + (amount -1) / 8);
247  to_send[10] = (uint8_t) (amount >> 8);
248  to_send[11] = (uint8_t) (amount >> 8);
249  to_send[12] = (uint8_t) ((amount + 7) / 8);
250  for(int i = 0; i < amount; i++) {
251  to_send[13 + (i - 1) / 8] += (uint8_t) (value[i] << (i % 8));
252  }
253  modbus_send(to_send, 14 + (amount - 1) / 8);
254  }
255 }
256 
257 
258 /**
259  * Read Request Builder and Sender
260  * @param address Reference Address
261  * @param amount Amount of Data to Read
262  * @param func Modbus Functional Code
263  */
264 
265 void modbus::modbus_read(int address, int amount, int func){
266  uint8_t to_send[12];
267  modbus_build_request(to_send, address, func);
268  to_send[5] = 6;
269  to_send[10] = (uint8_t) (amount >> 8);
270  to_send[11] = (uint8_t) (amount & 0x00FF);
271  modbus_send(to_send, 12);
272 }
273 
274 
275 /**
276  * Read Holding Registers
277  * MODBUS FUNCTION 0x03
278  * @param address Reference Address
279  * @param amount Amount of Registers to Read
280  * @param buffer Buffer to Store Data Read from Registers
281  */
282 
283 void modbus::modbus_read_holding_registers(int address, int amount, uint16_t *buffer) {
284  if(_connected) {
285  if(amount > 65535 || address > 65535) {
286  throw modbus_amount_exception();
287  }
288  modbus_read(address, amount, READ_REGS);
289  uint8_t to_rec[MAX_MSG_LENGTH];
290  modbus_receive(to_rec);
291  try {
293  for(int i = 0; i < amount; i++) {
294  buffer[i] = ((uint16_t)to_rec[9 + 2 * i]) << 8;
295  buffer[i] += (uint16_t) to_rec[10 + 2 * i];
296  }
297  } catch (std::exception &e) {
298  throw; //e;
299  }
300  } else {
301  throw modbus_connect_exception();
302  }
303 }
304 
305 
306 /**
307  * Read Input Registers
308  * MODBUS FUNCTION 0x04
309  * @param address Reference Address
310  * @param amount Amount of Registers to Read
311  * @param buffer Buffer to Store Data Read from Registers
312  */
313 
314 void modbus::modbus_read_input_registers(int address, int amount, uint16_t *buffer) {
315  if(_connected){
316  if(amount > 65535 || address > 65535) {
317  throw modbus_amount_exception();
318  }
319  modbus_read(address, amount, READ_INPUT_REGS);
320  uint8_t to_rec[MAX_MSG_LENGTH];
321  modbus_receive(to_rec);
322  try {
324  for(int i = 0; i < amount; i++) {
325  buffer[i] = ((uint16_t)to_rec[9 + 2 * i]) << 8;
326  buffer[i] += (uint16_t) to_rec[10 + 2 * i];
327  }
328  } catch (std::exception &e) {
329  throw; //e;
330  }
331  } else {
332  throw modbus_connect_exception();
333  }
334 }
335 
336 
337 /**
338  * Read Coils
339  * MODBUS FUNCTION 0x01
340  * @param address Reference Address
341  * @param amount Amount of Coils to Read
342  * @param buffer Buffer to Store Data Read from Coils
343  */
344 
345 void modbus::modbus_read_coils(int address, int amount, bool *buffer) {
346  if(_connected) {
347  if(amount > 2040 || address > 65535) {
348  throw modbus_amount_exception();
349  }
350  modbus_read(address, amount, READ_COILS);
351  uint8_t to_rec[MAX_MSG_LENGTH];
352  modbus_receive(to_rec);
353  try {
355  for(int i = 0; i < amount; i++) {
356  buffer[i] = (bool) ((to_rec[9 + i / 8] >> (i % 8)) & 1);
357  }
358  } catch (std::exception &e) {
359  throw; //e;
360  }
361  } else {
362  throw modbus_connect_exception();
363  }
364 }
365 
366 
367 /**
368  * Read Input Bits(Discrete Data)
369  * MODBUS FUNCITON 0x02
370  * @param address Reference Address
371  * @param amount Amount of Bits to Read
372  * @param buffer Buffer to store Data Read from Input Bits
373  */
374 
375 void modbus::modbus_read_input_bits(int address, int amount, bool* buffer) {
376  if(_connected) {
377  if(amount > 2040 || address > 65535) {
378  throw modbus_amount_exception();
379  }
380  modbus_read(address, amount, READ_INPUT_BITS);
381  uint8_t to_rec[MAX_MSG_LENGTH];
382  modbus_receive(to_rec);
383  try {
385  for(int i = 0; i < amount; i++) {
386  buffer[i] = (bool) ((to_rec[9 + i / 8] >> (i % 8)) & 1);
387  }
388  } catch (std::exception &e) {
389  throw; //e;
390  }
391  } else {
392  throw modbus_connect_exception();
393  }
394 }
395 
396 
397 /**
398  * Write Single Coils
399  * MODBUS FUNCTION 0x05
400  * @param address Reference Address
401  * @param to_write Value to be Written to Coil
402  */
403 
404 void modbus::modbus_write_coil(int address, bool to_write) {
405  if(_connected) {
406  if(address > 65535) {
407  throw modbus_amount_exception();
408  }
409  int value = to_write * 0xFF00;
410  modbus_write(address, 1, WRITE_COIL, (uint16_t *)&value);
411  uint8_t to_rec[MAX_MSG_LENGTH];
412  modbus_receive(to_rec);
413  try{
415  } catch (std::exception &e) {
416  throw; //e;
417  }
418  } else {
419  throw modbus_connect_exception();
420  }
421 }
422 
423 
424 /**
425  * Write Single Register
426  * FUCTION 0x06
427  * @param address Reference Address
428  * @param value Value to Be Written to Register
429  */
430 
431 void modbus::modbus_write_register(int address, uint16_t value) {
432  if(_connected) {
433  if(address > 65535) {
434  throw modbus_amount_exception();
435  }
436  modbus_write(address, 1, WRITE_REG, &value);
437  uint8_t to_rec[MAX_MSG_LENGTH];
438  modbus_receive(to_rec);
439  try{
441  } catch (std::exception &e) {
442  throw; //e;
443  }
444  } else {
445  throw modbus_connect_exception();
446  }
447 }
448 
449 
450 /**
451  * Write Multiple Coils
452  * MODBUS FUNCTION 0x0F
453  * @param address Reference Address
454  * @param amount Amount of Coils to Write
455  * @param value Values to Be Written to Coils
456  */
457 
458 void modbus::modbus_write_coils(int address, int amount, bool *value) {
459  if(_connected) {
460  if(address > 65535 || amount > 65535) {
461  throw modbus_amount_exception();
462  }
463  uint16_t temp[amount];
464  for(int i = 0; i < 4; i++) {
465  temp[i] = (uint16_t)value[i];
466  }
467  modbus_write(address, amount, WRITE_COILS, temp);
468  uint8_t to_rec[MAX_MSG_LENGTH];
469  modbus_receive(to_rec);
470  try{
472  } catch (std::exception &e) {
473  throw; //e;
474  }
475  } else {
476  throw modbus_connect_exception();
477  }
478 }
479 
480 
481 /**
482  * Write Multiple Registers
483  * MODBUS FUNCION 0x10
484  * @param address Reference Address
485  * @param amount Amount of Value to Write
486  * @param value Values to Be Written to the Registers
487  */
488 
489 void modbus::modbus_write_registers(int address, int amount, uint16_t *value) {
490  if(_connected) {
491  if(address > 65535 || amount > 65535) {
492  throw modbus_amount_exception();
493  }
494  modbus_write(address, amount, WRITE_REGS, value);
495  uint8_t to_rec[MAX_MSG_LENGTH];
496  modbus_receive(to_rec);
497  try{
499  } catch (std::exception &e) {
500  throw; //e;
501  }
502  } else {
503  throw modbus_connect_exception();
504  }
505 }
506 
507 
508 /**
509  * Data Sender
510  * @param to_send Request to Be Sent to Server
511  * @param length Length of the Request
512  * @return Size of the request
513  */
514 
515 ssize_t modbus::modbus_send(uint8_t *to_send, int length) {
516  _msg_id++;
517  return send(_socket, to_send, (size_t)length, 0);
518 }
519 
520 
521 /**
522  * Data Receiver
523  * @param buffer Buffer to Store the Data Retrieved
524  * @return Size of Incoming Data
525  */
526 
527 ssize_t modbus::modbus_receive(uint8_t *buffer) {
528  return recv(_socket, (char *) buffer, MAX_MSG_LENGTH, 0);
529 }
530 
531 
532 /**
533  * Error Code Handler
534  * @param msg Message Received from the Server
535  * @param func Modbus Functional Code
536  */
537 
538 void modbus::modbus_error_handle(uint8_t *msg, int func) {
539  if(msg[7] == func + 0x80) {
540  switch(msg[8]){
541  case EX_ILLEGAL_FUNCTION:
543  case EX_ILLEGAL_ADDRESS:
545  case EX_ILLEGAL_VALUE:
547  case EX_SERVER_FAILURE:
549  case EX_ACKNOWLEDGE:
551  case EX_SERVER_BUSY:
553  case EX_GATEWAY_PROBLEMP:
554  case EX_GATEWYA_PROBLEMF:
555  throw modbus_gateway_exception();
556  default:
557  break;
558  }
559  }
560 }
561 #endif
562 
563 #endif //MODBUSPP_MODBUS_H
Modbus Acknowledge Exception.
Modbus Amount Exception.
Modbus Connect Exception.
Modbus Gate Way Problem Exception.
Modbus Illegal Address Exception.
Modbus Illegal Data Value Exception.
Modbus Illgal Function Exception.
Modbus Server Busy Exception.
Modbus Server Failure Exception.
Modbus Operator Class.
Definition: modbus.hpp:63
int _msg_id
Definition: modbus.hpp:68
void modbus_read(int address, int amount, int func)
Definition: modbus.cpp:100
bool modbus_connect()
Definition: modbus.cpp:19
void modbus_read_input_registers(int address, int amount, uint16_t *buffer)
Definition: modbus.cpp:131
void modbus_read_input_bits(int address, int amount, bool *buffer)
Definition: modbus.cpp:174
void modbus_write_coils(int address, int amount, bool *value)
Definition: modbus.cpp:232
~modbus()
Definition: modbus.cpp:12
void modbus_read_holding_registers(int address, int amount, uint16_t *buffer)
Definition: modbus.cpp:109
bool _connected
Definition: modbus.hpp:65
ssize_t modbus_send(uint8_t *to_send, int length)
Definition: modbus.cpp:272
void modbus_build_request(uint8_t *to_send, int address, int func)
Definition: modbus.cpp:54
modbus(const std::string &host, uint16_t port)
Definition: modbus.cpp:6
void modbus_set_slave_id(int id)
Definition: modbus.cpp:15
ssize_t modbus_receive(uint8_t *buffer)
Definition: modbus.cpp:277
void modbus_write_coil(int address, bool to_write)
Definition: modbus.cpp:195
uint16_t PORT
Definition: modbus.hpp:66
void modbus_close()
Definition: modbus.cpp:49
struct sockaddr_in _server
Definition: modbus.hpp:71
int _socket
Definition: modbus.hpp:67
void modbus_write_register(int address, uint16_t value)
Definition: modbus.cpp:214
void modbus_error_handle(uint8_t *msg, int func)
Definition: modbus.cpp:281
std::string HOST
Definition: modbus.hpp:70
void modbus_write(int address, int amount, int func, uint16_t *value)
Definition: modbus.cpp:66
void modbus_write_registers(int address, int amount, uint16_t *value)
Definition: modbus.cpp:254
void modbus_read_coils(int address, int amount, bool *buffer)
Definition: modbus.cpp:153
int _slaveid
Definition: modbus.hpp:69
@ EX_SERVER_FAILURE
Definition: modbus.hpp:50
@ EX_GATEWAY_PROBLEMP
Definition: modbus.hpp:53
@ EX_ACKNOWLEDGE
Definition: modbus.hpp:51
@ EX_ILLEGAL_FUNCTION
Definition: modbus.hpp:47
@ EX_GATEWYA_PROBLEMF
Definition: modbus.hpp:54
@ EX_SERVER_BUSY
Definition: modbus.hpp:52
@ EX_ILLEGAL_VALUE
Definition: modbus.hpp:49
@ EX_ILLEGAL_ADDRESS
Definition: modbus.hpp:48
#define MAX_MSG_LENGTH
Definition: modbus.hpp:31
@ READ_COILS
Definition: modbus.hpp:35
@ WRITE_REG
Definition: modbus.hpp:40
@ WRITE_COIL
Definition: modbus.hpp:39
@ READ_INPUT_BITS
Definition: modbus.hpp:36
@ READ_REGS
Definition: modbus.hpp:37
@ WRITE_COILS
Definition: modbus.hpp:41
@ WRITE_REGS
Definition: modbus.hpp:42
@ READ_INPUT_REGS
Definition: modbus.hpp:38
GeneratorWrapper< T > value(T &&value)
Definition: catch.hpp:4001
std::stringstream msg