Commit f8480708371b7cd9bd6cc159c3412173d79ef6ae

Authored by Peter M. Groen
1 parent b8720844

Creating modbus PDU class

documentation/implementation.md 0 → 100644
documentation/protocol.md 0 → 100644
include/modbus.h deleted
1 -/*  
2 - * Copyright (c)2022 Peter M. Groen  
3 - *  
4 - * This source code is licensed under the MIT license found in the  
5 - * LICENSE file in hte root directory of this source tree  
6 - */  
7 -  
8 -#pragma once  
9 -  
10 -#include <cstring>  
11 -#include <stdint.h>  
12 -#include <string>  
13 -  
14 -#ifdef ENABLE_MODBUS_LOGGING  
15 -#include <cstdio>  
16 -#define LOG(fmt, ...) printf("[ modbuspp ]" fmt, ##__VA_ARGS__)  
17 -#else  
18 -#define LOG(...) (void)0  
19 -#endif  
20 -  
21 -#include <unistd.h>  
22 -#include <sys/socket.h>  
23 -#include <netinet/in.h>  
24 -#include <arpa/inet.h>  
25 -using X_SOCKET = int;  
26 -  
27 -#define X_ISVALIDSOCKET(s) ((s) >= 0)  
28 -#define X_CLOSE_SOCKET(s) close(s)  
29 -#define X_ISCONNECTSUCCEED(s) ((s) >= 0)  
30 -  
31 -using SOCKADDR = struct sockaddr;  
32 -using SOCKADDR_IN = struct sockaddr_in;  
33 -  
34 -#define MAX_MSG_LENGTH 260  
35 -  
36 -///Function Code  
37 -#define READ_COILS 0x01  
38 -#define READ_INPUT_BITS 0x02  
39 -#define READ_REGS 0x03  
40 -#define READ_INPUT_REGS 0x04  
41 -#define WRITE_COIL 0x05  
42 -#define WRITE_REG 0x06  
43 -#define WRITE_COILS 0x0F  
44 -#define WRITE_REGS 0x10  
45 -  
46 -///Exception Codes  
47 -  
48 -#define EX_ILLEGAL_FUNCTION 0x01 // Function Code not Supported  
49 -#define EX_ILLEGAL_ADDRESS 0x02 // Output Address not exists  
50 -#define EX_ILLEGAL_VALUE 0x03 // Output Value not in Range  
51 -#define EX_SERVER_FAILURE 0x04 // Slave Deive Fails to process request  
52 -#define EX_ACKNOWLEDGE 0x05 // Service Need Long Time to Execute  
53 -#define EX_SERVER_BUSY 0x06 // Server Was Unable to Accept MB Request PDU  
54 -#define EX_NEGATIVE_ACK 0x07  
55 -#define EX_MEM_PARITY_PROB 0x08  
56 -#define EX_GATEWAY_PROBLEMP 0x0A // Gateway Path not Available  
57 -#define EX_GATEWAY_PROBLEMF 0x0B // Target Device Failed to Response  
58 -#define EX_BAD_DATA 0XFF // Bad Data lenght or Address  
59 -  
60 -#define BAD_CON -1  
61 -  
62 -/**  
63 - * Modbus Operator Class  
64 - * Providing networking support and mobus operation support.  
65 - */  
66 -class modbus  
67 -{  
68 -  
69 -public:  
70 - bool err{};  
71 - int err_no{};  
72 - std::string error_msg;  
73 -  
74 - modbus(std::string host, uint16_t port);  
75 - ~modbus();  
76 -  
77 - bool modbus_connect();  
78 - void modbus_close() const;  
79 -  
80 - bool is_connected() const { return _connected; }  
81 -  
82 - void modbus_set_slave_id(int id);  
83 -  
84 - int modbus_read_coils(uint16_t address, uint16_t amount, bool *buffer);  
85 - int modbus_read_input_bits(uint16_t address, uint16_t amount, bool *buffer);  
86 - int modbus_read_holding_registers(uint16_t address, uint16_t amount, uint16_t *buffer);  
87 - int modbus_read_input_registers(uint16_t address, uint16_t amount, uint16_t *buffer);  
88 -  
89 - int modbus_write_coil(uint16_t address, const bool &to_write);  
90 - int modbus_write_register(uint16_t address, const uint16_t &value);  
91 - int modbus_write_coils(uint16_t address, uint16_t amount, const bool *value);  
92 - int modbus_write_registers(uint16_t address, uint16_t amount, const uint16_t *value);  
93 -  
94 -private:  
95 - bool _connected{};  
96 - uint16_t PORT{};  
97 - uint32_t _msg_id{};  
98 - int _slaveid{};  
99 - std::string HOST;  
100 -  
101 - X_SOCKET _socket{};  
102 - SOCKADDR_IN _server{};  
103 -  
104 - void modbus_build_request(uint8_t *to_send, uint16_t address, int func) const;  
105 -  
106 - int modbus_read(uint16_t address, uint16_t amount, int func);  
107 - int modbus_write(uint16_t address, uint16_t amount, int func, const uint16_t *value);  
108 -  
109 - ssize_t modbus_send(uint8_t *to_send, size_t length);  
110 - ssize_t modbus_receive(uint8_t *buffer) const;  
111 -  
112 - void modbuserror_handle(const uint8_t *msg, int func);  
113 -  
114 - void set_bad_con();  
115 - void set_bad_input();  
116 -};  
117 -  
118 -/**  
119 - * Main Constructor of Modbus Connector Object  
120 - * @param host IP Address of Host  
121 - * @param port Port for the TCP Connection  
122 - * @return A Modbus Connector Object  
123 - */  
124 -inline modbus::modbus(std::string host, uint16_t port = 502)  
125 -{  
126 - HOST = host;  
127 - PORT = port;  
128 - _slaveid = 1;  
129 - _msg_id = 1;  
130 - _connected = false;  
131 - err = false;  
132 - err_no = 0;  
133 - error_msg = "";  
134 -}  
135 -  
136 -/**  
137 - * Destructor of Modbus Connector Object  
138 - */  
139 -inline modbus::~modbus(void) = default;  
140 -  
141 -/**  
142 - * Modbus Slave ID Setter  
143 - * @param id ID of the Modbus Server Slave  
144 - */  
145 -inline void modbus::modbus_set_slave_id(int id)  
146 -{  
147 - _slaveid = id;  
148 -}  
149 -  
150 -/**  
151 - * Build up a Modbus/TCP Connection  
152 - * @return If A Connection Is Successfully Built  
153 - */  
154 -inline bool modbus::modbus_connect()  
155 -{  
156 - if (HOST.empty() || PORT == 0)  
157 - {  
158 - LOG("Missing Host and Port");  
159 - return false;  
160 - }  
161 - else  
162 - {  
163 - LOG("Found Proper Host %s and Port %d", HOST.c_str(), PORT);  
164 - }  
165 -  
166 - _socket = socket(AF_INET, SOCK_STREAM, 0);  
167 - if (!X_ISVALIDSOCKET(_socket))  
168 - {  
169 - LOG("Error Opening Socket");  
170 - return false;  
171 - }  
172 - else  
173 - {  
174 - LOG("Socket Opened Successfully");  
175 - }  
176 -  
177 - struct timeval timeout  
178 - {  
179 - };  
180 - timeout.tv_sec = 20; // after 20 seconds connect() will timeout  
181 - timeout.tv_usec = 0;  
182 -  
183 - setsockopt(_socket, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(timeout));  
184 - setsockopt(_socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout));  
185 - _server.sin_family = AF_INET;  
186 - _server.sin_addr.s_addr = inet_addr(HOST.c_str());  
187 - _server.sin_port = htons(PORT);  
188 -  
189 - if (!X_ISCONNECTSUCCEED(connect(_socket, (SOCKADDR *)&_server, sizeof(_server))))  
190 - {  
191 - LOG("Connection Error");  
192 - return false;  
193 - }  
194 -  
195 - LOG("Connected");  
196 - _connected = true;  
197 - return true;  
198 -}  
199 -  
200 -/**  
201 - * Close the Modbus/TCP Connection  
202 - */  
203 -inline void modbus::modbus_close() const  
204 -{  
205 - X_CLOSE_SOCKET(_socket);  
206 - LOG("Socket Closed");  
207 -}  
208 -  
209 -/**  
210 - * Modbus Request Builder  
211 - * @param to_send Message Buffer to Be Sent  
212 - * @param address Reference Address  
213 - * @param func Modbus Functional Code  
214 - */  
215 -inline void modbus::modbus_build_request(uint8_t *to_send, uint16_t address, int func) const  
216 -{  
217 - to_send[0] = (uint8_t)(_msg_id >> 8u);  
218 - to_send[1] = (uint8_t)(_msg_id & 0x00FFu);  
219 - to_send[2] = 0;  
220 - to_send[3] = 0;  
221 - to_send[4] = 0;  
222 - to_send[6] = (uint8_t)_slaveid;  
223 - to_send[7] = (uint8_t)func;  
224 - to_send[8] = (uint8_t)(address >> 8u);  
225 - to_send[9] = (uint8_t)(address & 0x00FFu);  
226 -}  
227 -  
228 -/**  
229 - * Write Request Builder and Sender  
230 - * @param address Reference Address  
231 - * @param amount Amount of data to be Written  
232 - * @param func Modbus Functional Code  
233 - * @param value Data to Be Written  
234 - */  
235 -inline int modbus::modbus_write(uint16_t address, uint16_t amount, int func, const uint16_t *value)  
236 -{  
237 - int status = 0;  
238 - uint8_t *to_send;  
239 - if (func == WRITE_COIL || func == WRITE_REG)  
240 - {  
241 - to_send = new uint8_t[12];  
242 - modbus_build_request(to_send, address, func);  
243 - to_send[5] = 6;  
244 - to_send[10] = (uint8_t)(value[0] >> 8u);  
245 - to_send[11] = (uint8_t)(value[0] & 0x00FFu);  
246 - status = modbus_send(to_send, 12);  
247 - }  
248 - else if (func == WRITE_REGS)  
249 - {  
250 - to_send = new uint8_t[13 + 2 * amount];  
251 - modbus_build_request(to_send, address, func);  
252 - to_send[5] = (uint8_t)(7 + 2 * amount);  
253 - to_send[10] = (uint8_t)(amount >> 8u);  
254 - to_send[11] = (uint8_t)(amount & 0x00FFu);  
255 - to_send[12] = (uint8_t)(2 * amount);  
256 - for (int i = 0; i < amount; i++)  
257 - {  
258 - to_send[13 + 2 * i] = (uint8_t)(value[i] >> 8u);  
259 - to_send[14 + 2 * i] = (uint8_t)(value[i] & 0x00FFu);  
260 - }  
261 - status = modbus_send(to_send, 13 + 2 * amount);  
262 - }  
263 - else if (func == WRITE_COILS)  
264 - {  
265 - to_send = new uint8_t[14 + (amount - 1) / 8];  
266 - modbus_build_request(to_send, address, func);  
267 - to_send[5] = (uint8_t)(7 + (amount + 7) / 8);  
268 - to_send[10] = (uint8_t)(amount >> 8u);  
269 - to_send[11] = (uint8_t)(amount & 0x00FFu);  
270 - to_send[12] = (uint8_t)((amount + 7) / 8);  
271 - for (int i = 0; i < (amount + 7) / 8; i++)  
272 - to_send[13 + i] = 0; // init needed before summing!  
273 - for (int i = 0; i < amount; i++)  
274 - {  
275 - to_send[13 + i / 8] += (uint8_t)(value[i] << (i % 8u));  
276 - }  
277 - status = modbus_send(to_send, 14 + (amount - 1) / 8);  
278 - }  
279 - delete[] to_send;  
280 - return status;  
281 -}  
282 -  
283 -/**  
284 - * Read Request Builder and Sender  
285 - * @param address Reference Address  
286 - * @param amount Amount of Data to Read  
287 - * @param func Modbus Functional Code  
288 - */  
289 -inline int modbus::modbus_read(uint16_t address, uint16_t amount, int func)  
290 -{  
291 - uint8_t to_send[12];  
292 - modbus_build_request(to_send, address, func);  
293 - to_send[5] = 6;  
294 - to_send[10] = (uint8_t)(amount >> 8u);  
295 - to_send[11] = (uint8_t)(amount & 0x00FFu);  
296 - return modbus_send(to_send, 12);  
297 -}  
298 -  
299 -/**  
300 - * Read Holding Registers  
301 - * MODBUS FUNCTION 0x03  
302 - * @param address Reference Address  
303 - * @param amount Amount of Registers to Read  
304 - * @param buffer Buffer to Store Data Read from Registers  
305 - */  
306 -inline int modbus::modbus_read_holding_registers(uint16_t address, uint16_t amount, uint16_t *buffer)  
307 -{  
308 - if (_connected)  
309 - {  
310 - modbus_read(address, amount, READ_REGS);  
311 - uint8_t to_rec[MAX_MSG_LENGTH];  
312 - ssize_t k = modbus_receive(to_rec);  
313 - if (k == -1)  
314 - {  
315 - set_bad_con();  
316 - return BAD_CON;  
317 - }  
318 - modbuserror_handle(to_rec, READ_REGS);  
319 - if (err)  
320 - return err_no;  
321 - for (auto i = 0; i < amount; i++)  
322 - {  
323 - buffer[i] = ((uint16_t)to_rec[9u + 2u * i]) << 8u;  
324 - buffer[i] += (uint16_t)to_rec[10u + 2u * i];  
325 - }  
326 - return 0;  
327 - }  
328 - else  
329 - {  
330 - set_bad_con();  
331 - return BAD_CON;  
332 - }  
333 -}  
334 -  
335 -/**  
336 - * Read Input Registers  
337 - * MODBUS FUNCTION 0x04  
338 - * @param address Reference Address  
339 - * @param amount Amount of Registers to Read  
340 - * @param buffer Buffer to Store Data Read from Registers  
341 - */  
342 -inline int modbus::modbus_read_input_registers(uint16_t address, uint16_t amount, uint16_t *buffer)  
343 -{  
344 - if (_connected)  
345 - {  
346 - modbus_read(address, amount, READ_INPUT_REGS);  
347 - uint8_t to_rec[MAX_MSG_LENGTH];  
348 - ssize_t k = modbus_receive(to_rec);  
349 - if (k == -1)  
350 - {  
351 - set_bad_con();  
352 - return BAD_CON;  
353 - }  
354 - modbuserror_handle(to_rec, READ_INPUT_REGS);  
355 - if (err)  
356 - return err_no;  
357 - for (auto i = 0; i < amount; i++)  
358 - {  
359 - buffer[i] = ((uint16_t)to_rec[9u + 2u * i]) << 8u;  
360 - buffer[i] += (uint16_t)to_rec[10u + 2u * i];  
361 - }  
362 - return 0;  
363 - }  
364 - else  
365 - {  
366 - set_bad_con();  
367 - return BAD_CON;  
368 - }  
369 -}  
370 -  
371 -/**  
372 - * Read Coils  
373 - * MODBUS FUNCTION 0x01  
374 - * @param address Reference Address  
375 - * @param amount Amount of Coils to Read  
376 - * @param buffer Buffer to Store Data Read from Coils  
377 - */  
378 -inline int modbus::modbus_read_coils(uint16_t address, uint16_t amount, bool *buffer)  
379 -{  
380 - if (_connected)  
381 - {  
382 - if (amount > 2040)  
383 - {  
384 - set_bad_input();  
385 - return EX_BAD_DATA;  
386 - }  
387 - modbus_read(address, amount, READ_COILS);  
388 - uint8_t to_rec[MAX_MSG_LENGTH];  
389 - ssize_t k = modbus_receive(to_rec);  
390 - if (k == -1)  
391 - {  
392 - set_bad_con();  
393 - return BAD_CON;  
394 - }  
395 - modbuserror_handle(to_rec, READ_COILS);  
396 - if (err)  
397 - return err_no;  
398 - for (auto i = 0; i < amount; i++)  
399 - {  
400 - buffer[i] = (bool)((to_rec[9u + i / 8u] >> (i % 8u)) & 1u);  
401 - }  
402 - return 0;  
403 - }  
404 - else  
405 - {  
406 - set_bad_con();  
407 - return BAD_CON;  
408 - }  
409 -}  
410 -  
411 -/**  
412 - * Read Input Bits(Discrete Data)  
413 - * MODBUS FUNCITON 0x02  
414 - * @param address Reference Address  
415 - * @param amount Amount of Bits to Read  
416 - * @param buffer Buffer to store Data Read from Input Bits  
417 - */  
418 -inline int modbus::modbus_read_input_bits(uint16_t address, uint16_t amount, bool *buffer)  
419 -{  
420 - if (_connected)  
421 - {  
422 - if (amount > 2040)  
423 - {  
424 - set_bad_input();  
425 - return EX_BAD_DATA;  
426 - }  
427 - modbus_read(address, amount, READ_INPUT_BITS);  
428 - uint8_t to_rec[MAX_MSG_LENGTH];  
429 - ssize_t k = modbus_receive(to_rec);  
430 - if (k == -1)  
431 - {  
432 - set_bad_con();  
433 - return BAD_CON;  
434 - }  
435 - if (err)  
436 - return err_no;  
437 - for (auto i = 0; i < amount; i++)  
438 - {  
439 - buffer[i] = (bool)((to_rec[9u + i / 8u] >> (i % 8u)) & 1u);  
440 - }  
441 - modbuserror_handle(to_rec, READ_INPUT_BITS);  
442 - return 0;  
443 - }  
444 - else  
445 - {  
446 - return BAD_CON;  
447 - }  
448 -}  
449 -  
450 -/**  
451 - * Write Single Coils  
452 - * MODBUS FUNCTION 0x05  
453 - * @param address Reference Address  
454 - * @param to_write Value to be Written to Coil  
455 - */  
456 -inline int modbus::modbus_write_coil(uint16_t address, const bool &to_write)  
457 -{  
458 - if (_connected)  
459 - {  
460 - int value = to_write * 0xFF00;  
461 - modbus_write(address, 1, WRITE_COIL, (uint16_t *)&value);  
462 - uint8_t to_rec[MAX_MSG_LENGTH];  
463 - ssize_t k = modbus_receive(to_rec);  
464 - if (k == -1)  
465 - {  
466 - set_bad_con();  
467 - return BAD_CON;  
468 - }  
469 - modbuserror_handle(to_rec, WRITE_COIL);  
470 - if (err)  
471 - return err_no;  
472 - return 0;  
473 - }  
474 - else  
475 - {  
476 - set_bad_con();  
477 - return BAD_CON;  
478 - }  
479 -}  
480 -  
481 -/**  
482 - * Write Single Register  
483 - * FUCTION 0x06  
484 - * @param address Reference Address  
485 - * @param value Value to Be Written to Register  
486 - */  
487 -inline int modbus::modbus_write_register(uint16_t address, const uint16_t &value)  
488 -{  
489 - if (_connected)  
490 - {  
491 - modbus_write(address, 1, WRITE_REG, &value);  
492 - uint8_t to_rec[MAX_MSG_LENGTH];  
493 - ssize_t k = modbus_receive(to_rec);  
494 - if (k == -1)  
495 - {  
496 - set_bad_con();  
497 - return BAD_CON;  
498 - }  
499 - modbuserror_handle(to_rec, WRITE_COIL);  
500 - if (err)  
501 - return err_no;  
502 - return 0;  
503 - }  
504 - else  
505 - {  
506 - set_bad_con();  
507 - return BAD_CON;  
508 - }  
509 -}  
510 -  
511 -/**  
512 - * Write Multiple Coils  
513 - * MODBUS FUNCTION 0x0F  
514 - * @param address Reference Address  
515 - * @param amount Amount of Coils to Write  
516 - * @param value Values to Be Written to Coils  
517 - */  
518 -inline int modbus::modbus_write_coils(uint16_t address, uint16_t amount, const bool *value)  
519 -{  
520 - if (_connected)  
521 - {  
522 - uint16_t *temp = new uint16_t[amount];  
523 - for (int i = 0; i < amount; i++)  
524 - {  
525 - temp[i] = (uint16_t)value[i];  
526 - }  
527 - modbus_write(address, amount, WRITE_COILS, temp);  
528 - delete[] temp;  
529 - uint8_t to_rec[MAX_MSG_LENGTH];  
530 - ssize_t k = modbus_receive(to_rec);  
531 - if (k == -1)  
532 - {  
533 - set_bad_con();  
534 - return BAD_CON;  
535 - }  
536 - modbuserror_handle(to_rec, WRITE_COILS);  
537 - if (err)  
538 - return err_no;  
539 - return 0;  
540 - }  
541 - else  
542 - {  
543 - set_bad_con();  
544 - return BAD_CON;  
545 - }  
546 -}  
547 -  
548 -/**  
549 - * Write Multiple Registers  
550 - * MODBUS FUNCION 0x10  
551 - * @param address Reference Address  
552 - * @param amount Amount of Value to Write  
553 - * @param value Values to Be Written to the Registers  
554 - */  
555 -inline int modbus::modbus_write_registers(uint16_t address, uint16_t amount, const uint16_t *value)  
556 -{  
557 - if (_connected)  
558 - {  
559 - modbus_write(address, amount, WRITE_REGS, value);  
560 - uint8_t to_rec[MAX_MSG_LENGTH];  
561 - ssize_t k = modbus_receive(to_rec);  
562 - if (k == -1)  
563 - {  
564 - set_bad_con();  
565 - return BAD_CON;  
566 - }  
567 - modbuserror_handle(to_rec, WRITE_REGS);  
568 - if (err)  
569 - return err_no;  
570 - return 0;  
571 - }  
572 - else  
573 - {  
574 - set_bad_con();  
575 - return BAD_CON;  
576 - }  
577 -}  
578 -  
579 -/**  
580 - * Data Sender  
581 - * @param to_send Request to Be Sent to Server  
582 - * @param length Length of the Request  
583 - * @return Size of the request  
584 - */  
585 -inline ssize_t modbus::modbus_send(uint8_t *to_send, size_t length)  
586 -{  
587 - _msg_id++;  
588 - return send(_socket, (const char *)to_send, (size_t)length, 0);  
589 -}  
590 -  
591 -/**  
592 - * Data Receiver  
593 - * @param buffer Buffer to Store the Data Retrieved  
594 - * @return Size of Incoming Data  
595 - */  
596 -inline ssize_t modbus::modbus_receive(uint8_t *buffer) const  
597 -{  
598 - return recv(_socket, (char *)buffer, MAX_MSG_LENGTH, 0);  
599 -}  
600 -  
601 -inline void modbus::set_bad_con()  
602 -{  
603 - err = true;  
604 - error_msg = "BAD CONNECTION";  
605 -}  
606 -  
607 -inline void modbus::set_bad_input()  
608 -{  
609 - err = true;  
610 - error_msg = "BAD FUNCTION INPUT";  
611 -}  
612 -  
613 -/**  
614 - * Error Code Handler  
615 - * @param msg Message Received from the Server  
616 - * @param func Modbus Functional Code  
617 - */  
618 -inline void modbus::modbuserror_handle(const uint8_t *msg, int func)  
619 -{  
620 - err = false;  
621 - error_msg = "NO ERR";  
622 - if (msg[7] == func + 0x80)  
623 - {  
624 - err = true;  
625 - switch (msg[8])  
626 - {  
627 - case EX_ILLEGAL_FUNCTION:  
628 - error_msg = "1 Illegal Function";  
629 - break;  
630 - case EX_ILLEGAL_ADDRESS:  
631 - error_msg = "2 Illegal Address";  
632 - break;  
633 - case EX_ILLEGAL_VALUE:  
634 - error_msg = "3 Illegal Value";  
635 - break;  
636 - case EX_SERVER_FAILURE:  
637 - error_msg = "4 Server Failure";  
638 - break;  
639 - case EX_ACKNOWLEDGE:  
640 - error_msg = "5 Acknowledge";  
641 - break;  
642 - case EX_SERVER_BUSY:  
643 - error_msg = "6 Server Busy";  
644 - break;  
645 - case EX_NEGATIVE_ACK:  
646 - error_msg = "7 Negative Acknowledge";  
647 - break;  
648 - case EX_MEM_PARITY_PROB:  
649 - error_msg = "8 Memory Parity Problem";  
650 - break;  
651 - case EX_GATEWAY_PROBLEMP:  
652 - error_msg = "10 Gateway Path Unavailable";  
653 - break;  
654 - case EX_GATEWAY_PROBLEMF:  
655 - error_msg = "11 Gateway Target Device Failed to Respond";  
656 - break;  
657 - default:  
658 - error_msg = "UNK";  
659 - break;  
660 - }  
661 - }  
662 -}  
663 -  
src/modbusbase.cpp 0 → 100644
  1 +/*
  2 + * Copyright (c) 2022 Peter M. Groen
  3 + *
  4 + * This source code is licensed under the MIT license found in the
  5 + * LICENSE file in the root directory of this source tree.
  6 + */
  7 +
  8 +#include "modbusbase.h"
  9 +
  10 +
src/modbusbase.h 0 → 100644
  1 +/*
  2 + * Copyright (c) 2022 Peter M. Groen
  3 + *
  4 + * This source code is licensed under the MIT license found in the
  5 + * LICENSE file in the root directory of this source tree.
  6 + */
  7 +
  8 +#pragma once
  9 +
  10 +// Create a simple logger for console output during debugging.
  11 +// TODO: Replace with a custom logger by using std::function
  12 +// to keep it consistent with TrueMQTT.
  13 +#ifdef ENABLE_MODBUS_LOGGING
  14 +#include <cstdio>
  15 +#define LOG(fmt, ...) printf("[ modbus-cpp ]" fmt, ##__VA_ARGS__)
  16 +#else
  17 +#define LOG(...) (void)0
  18 +#endif
  19 +
  20 +#define MAX_MSG_LENGTH 260
  21 +
  22 +// Function Codes
  23 +#define READ_COILS 0x01
  24 +#define READ_INPUT_BITS 0x02
  25 +#define READ_REGS 0x03
  26 +#define READ_INPUT_REGS 0x04
  27 +#define WRITE_COIL 0x05
  28 +#define WRITE_REG 0x06
  29 +#define WRITE_COILS 0x0F
  30 +#define WRITE_REGS 0x10
  31 +
  32 +// Exception codes
  33 +#define EX_ILLEGAL_FUNCTION 0x01
  34 +#define EX_ILLEGAL_ADDRESS 0x02
  35 +#define EX_ILLEGAL_VALUE 0x03
  36 +#define EX_SERVER_FAILURE 0x04
  37 +#define EX_ACKNOWLEDGE 0x05
  38 +#define EX_SERVER_BUSY 0x06
  39 +#define EX_NEGATIVE_ACK 0x07
  40 +#define EX_MEM_PARITY_PROB 0x08
  41 +#define EX_GATEWAY_PROBLEMP 0x0A
  42 +#define EX_GATEWAY_PROBLEMF 0x0B
  43 +#define EX_BAD_DATA 0xFF
  44 +
  45 +#define BAD_CON -1
  46 +
  47 +// Modbus base class. Providing all Modbus PDUs without transport implementation
  48 +class ModbusBase
  49 +{
  50 +public:
  51 + bool err{};
  52 + int err_no{};
  53 + std::string m_error_msg;
  54 +
  55 + ModbusBase();
  56 + virtual ~ModbusBase();
  57 +
  58 + void setSlaveId(int slave_id);
  59 +
  60 + // Pure virtuals. Override when inherited.
  61 + virtual bool Connect() const = 0;
  62 + virtual void Close() = 0;
  63 + virtual bool isConnected() const - 0;
  64 +
  65 + // Modbus implementation(s)
  66 + int readCoils(uint16_t address, uint16_t amount, bool *buffer); // Replace buffer with something sensible?
  67 + int readInputBits(uint16_t address, uint16_t amount, bool *buffer); // Replace buffer with something sensible?
  68 + int readHoldingRegisters(uint16_t address, uitn16_t amount, uint16_t *buffer);
  69 + int readInputRegisters(uint16_t address, uint16_t amount, uint16_t *buffer);
  70 +
  71 + int writeCoil(uint16_t address, const bool &to_write);
  72 + int writeRegister(uint16_t address, const uint16_t &value);
  73 + int writeCoils(uint16_t address, uint16_t amount, const bool *value);
  74 + int writeRegisters(uint16_t address, uint16_t amount, const uint16_t *value);
  75 +
  76 +private: // Methods
  77 + void buildRequest(uint8_t *to_send, uint16_t address, int function_code) const;
  78 + int modbusRead(uint16_t address, uint16_t amount, int function_code);
  79 + int modbusWrite(uint16_t address, uint16_t amount, int function_code, const uint16_t *value);
  80 + ssize_t modbusSend(uint8_t *to_send, size_t length);
  81 + ssize_t modbusReceive(uint8_t *buffer) const;
  82 +
  83 + void modbusErrorHandle(const uint8_t *msg, int function_code);
  84 + void setBadConnection();
  85 + void setBadInput();
  86 +
  87 +private: // Members (Giggity!)
  88 + bool m_connected{};
  89 + uint32_t m_msg_id{};
  90 + int m_slaveId{};
  91 +
  92 +};