API
 
Loading...
Searching...
No Matches
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
34enum{
35 READ_COILS = 0x01,
37 READ_REGS = 0x03,
39 WRITE_COIL = 0x05,
40 WRITE_REG = 0x06,
42 WRITE_REGS = 0x10,
43};
44
45///Exception Codes
46enum {
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 */
63class modbus {
64private:
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
84public:
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
116modbus::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
127modbus::modbus(const std::string & host) : HOST{host} {}
128
129
130/**
131 * Destructor of Modbus Connector Object
132 */
133
134modbus::~modbus(void) {
135}
136
137
138/**
139 * Modbus Slave ID Setter
140 * @param id ID of the Modbus Server Slave
141 */
142
143void 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
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
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
202void 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
223void 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
265void 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
283void modbus::modbus_read_holding_registers(int address, int amount, uint16_t *buffer) {
284 if(_connected) {
285 if(amount > 65535 || address > 65535) {
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 {
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
314void modbus::modbus_read_input_registers(int address, int amount, uint16_t *buffer) {
315 if(_connected){
316 if(amount > 65535 || address > 65535) {
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 {
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
345void modbus::modbus_read_coils(int address, int amount, bool *buffer) {
346 if(_connected) {
347 if(amount > 2040 || address > 65535) {
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 {
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
375void modbus::modbus_read_input_bits(int address, int amount, bool* buffer) {
376 if(_connected) {
377 if(amount > 2040 || address > 65535) {
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 {
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
404void modbus::modbus_write_coil(int address, bool to_write) {
405 if(_connected) {
406 if(address > 65535) {
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 {
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
431void modbus::modbus_write_register(int address, uint16_t value) {
432 if(_connected) {
433 if(address > 65535) {
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 {
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
458void modbus::modbus_write_coils(int address, int amount, bool *value) {
459 if(_connected) {
460 if(address > 65535 || amount > 65535) {
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 {
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
489void modbus::modbus_write_registers(int address, int amount, uint16_t *value) {
490 if(_connected) {
491 if(address > 65535 || amount > 65535) {
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 {
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
515ssize_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
527ssize_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
538void modbus::modbus_error_handle(uint8_t *msg, int func) {
539 if(msg[7] == func + 0x80) {
540 switch(msg[8]){
545 case EX_ILLEGAL_VALUE:
549 case EX_ACKNOWLEDGE:
551 case EX_SERVER_BUSY:
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