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