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 +};
... ...