My Project
modbus.h
1 /*
2  * uuid-modbus - Microcontroller asynchronous Modbus library
3  * Copyright 2021-2022 Simon Arlott
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #ifndef UUID_MODBUS_H_
20 #define UUID_MODBUS_H_
21 
22 #include <Arduino.h>
23 
24 #include <cstdarg>
25 #include <cstdint>
26 #include <array>
27 #include <deque>
28 #include <memory>
29 #include <vector>
30 
31 #include <uuid/log.h>
32 
33 namespace uuid {
34 
45 namespace modbus {
46 
47 constexpr uint16_t MAX_MESSAGE_SIZE = 256;
48 constexpr uint16_t MESSAGE_HEADER_SIZE = 2;
49 constexpr uint16_t MESSAGE_CRC_SIZE = 2;
61 constexpr uint32_t INTER_FRAME_TIMEOUT_MS = 5;
62 constexpr uint16_t DEFAULT_UNICAST_TIMEOUT_MS = 10000;
63 constexpr uint16_t DEFAULT_BROADCAST_TIMEOUT_MS = 1000;
65 extern const uuid::log::Logger logger;
67 using frame_buffer_t = std::array<uint8_t, MAX_MESSAGE_SIZE + 1>;
76 enum DeviceAddressType : uint8_t {
77  BROADCAST = 0,
79  MAX_UNICAST = 247,
80 };
81 
87 enum FunctionCode : uint8_t {
92 };
93 
99 enum ResponseStatus : uint8_t {
114 };
115 
124 class Response {
125 public:
126  virtual ~Response() = default;
127 
134  inline bool done() const { return status_ >= ResponseStatus::SUCCESS; }
135 
142  inline bool pending() const { return status_ < ResponseStatus::SUCCESS; }
143 
150  bool success() const { return status_ == ResponseStatus::SUCCESS; }
151 
158  bool exception() const { return status_ == ResponseStatus::EXCEPTION; }
159 
167  bool failed() const { return status_ > ResponseStatus::EXCEPTION; }
168 
175  inline ResponseStatus status() const { return status_; }
176 
184 
194  inline uint8_t exception_code() const { return exception_code_; }
195 
206 
215  virtual ResponseStatus parse(frame_buffer_t &frame, uint16_t len) = 0;
216 
217 protected:
218  Response() = default;
219 
230  bool check_length(frame_buffer_t &frame, uint16_t actual, uint16_t expected);
231 
232 private:
233  ResponseStatus status_ = ResponseStatus::QUEUED;
234  uint8_t exception_code_ = 0;
235 };
236 
246 public:
256  inline const std::vector<uint16_t>& data() const { return data_; };
257 
266  ResponseStatus parse(frame_buffer_t &frame, uint16_t len) override;
267 
268 protected:
269  std::vector<uint16_t> data_;
270 };
271 
281 public:
290  ResponseStatus parse(frame_buffer_t &frame, uint16_t len) override;
291 
301  uint16_t address() const { return address_; }
302 
303 private:
304  uint16_t address_;
305 };
306 
316 public:
325  ResponseStatus parse(frame_buffer_t &frame, uint16_t len) override;
326 
335  inline uint8_t data() const { return data_; };
336 
337 private:
338  uint8_t data_;
339 };
340 
349 class Request {
350 public:
351  virtual ~Request() = default;
352 
353 public:
363  Request(uint16_t device, uint8_t function_code, uint16_t timeout_ms,
364  const std::shared_ptr<Response> &response);
365 
373  virtual uint16_t encode(frame_buffer_t &frame);
374 
381  inline uint16_t device() const { return device_; };
382 
389  inline uint8_t function_code() const { return function_code_; };
390 
397  inline uint16_t timeout_ms() const { return timeout_ms_; };
398 
405  inline Response& response() const { return *response_.get(); };
406 
407 private:
408  const uint16_t device_;
409  const uint8_t function_code_;
410  const uint16_t timeout_ms_;
411  const std::shared_ptr<Response> response_;
412 };
413 
422 class RegisterRequest: public Request {
423 public:
435  RegisterRequest(uint16_t device, uint8_t function_code, uint16_t timeout_ms,
436  uint16_t address, uint16_t data,
437  const std::shared_ptr<Response> &response);
438 
446  uint16_t encode(frame_buffer_t &frame) override;
447 
454  inline uint16_t address() const { return address_; };
455 
462  inline uint16_t data() const { return data_; };
463 
464 private:
465  const uint16_t address_;
466  const uint16_t data_;
467 };
468 
475 public:
482  SerialClient(::HardwareSerial &serial);
483 
484  ~SerialClient() = default;
485 
491  void loop();
492 
499  inline uint16_t default_unicast_timeout_ms() const { return default_unicast_timeout_ms_; }
506  inline void default_unicast_timeout_ms(uint16_t timeout_ms) { default_unicast_timeout_ms_ = timeout_ms; }
507 
514  inline uint16_t default_broadcast_delay_ms() const { return default_broadcast_timeout_ms_; }
521  inline void default_broadcast_delay_ms(uint16_t timeout_ms) { default_broadcast_timeout_ms_ = timeout_ms; }
522 
536  std::shared_ptr<const RegisterDataResponse> read_holding_registers(uint16_t device,
537  uint16_t address, uint16_t size, uint16_t timeout_ms = 0);
538 
552  std::shared_ptr<const RegisterDataResponse> read_input_registers(uint16_t device,
553  uint16_t address, uint16_t size, uint16_t timeout_ms = 0);
554 
571  std::shared_ptr<const RegisterWriteResponse> write_holding_register(uint16_t device,
572  uint16_t address, uint16_t value, uint16_t timeout_ms = 0);
573 
583  std::shared_ptr<const ExceptionStatusResponse> read_exception_status(uint16_t device,
584  uint16_t timeout_ms = 0);
585 
586 private:
592  void idle();
593 
599  void encode();
600 
606  void transmit();
607 
614  uint32_t input();
615 
622  void receive();
623 
629  void complete();
630 
637  void log_frame(const __FlashStringHelper *prefix);
638 
645  uint16_t calc_crc() const;
646 
647  ::HardwareSerial &serial_;
648  std::deque<std::unique_ptr<Request>> requests_;
652  bool idle_frame_ = false;
655  uint16_t frame_pos_ = 0;
657  uint32_t last_rx_ms_ = 0;
659  uint16_t tx_frame_size_ = 0;
660  uint32_t last_tx_ms_ = 0;
661 };
662 
663 } // namespace modbus
664 
665 } // namespace uuid
666 
667 #endif
uuid::modbus::FAILURE_FUNCTION
@ FAILURE_FUNCTION
Definition: modbus.h:111
uuid::log::Logger
Logger instance used to make log messages.
Definition: log.h:347
uuid::modbus::frame_buffer_t
std::array< uint8_t, MAX_MESSAGE_SIZE+1 > frame_buffer_t
Definition: modbus.h:67
uuid::modbus::Response::pending
bool pending() const
Determine if the request is still pending.
Definition: modbus.h:142
uuid::modbus::MAX_MESSAGE_SIZE
constexpr uint16_t MAX_MESSAGE_SIZE
Definition: modbus.h:47
uuid::modbus::RegisterRequest::data_
const uint16_t data_
Definition: modbus.h:466
uuid::modbus::SerialClient::last_rx_ms_
uint32_t last_rx_ms_
Definition: modbus.h:657
uuid::modbus::FAILURE_TIMEOUT
@ FAILURE_TIMEOUT
Definition: modbus.h:107
uuid::modbus::Request::encode
virtual uint16_t encode(frame_buffer_t &frame)
Encode this request and store it in a message frame buffer.
Definition: generic_functions.cpp:38
uuid::modbus::SUCCESS
@ SUCCESS
Definition: modbus.h:103
uuid::modbus::ExceptionStatusResponse::data_
uint8_t data_
Definition: modbus.h:335
uuid::modbus::MIN_UNICAST
@ MIN_UNICAST
Definition: modbus.h:78
uuid::modbus::Request
Request message.
Definition: modbus.h:349
uuid::modbus::Request::function_code_
const uint8_t function_code_
Definition: modbus.h:409
uuid::modbus::SerialClient::write_holding_register
std::shared_ptr< const RegisterWriteResponse > write_holding_register(uint16_t device, uint16_t address, uint16_t value, uint16_t timeout_ms=0)
Write to a single holding register in a remote device.
Definition: register_functions.cpp:76
uuid::modbus::Request::timeout_ms_
const uint16_t timeout_ms_
Definition: modbus.h:410
uuid::modbus::Response::status
void status(ResponseStatus status)
Set the status of the response message.
Definition: modbus.h:183
uuid::modbus::INTER_FRAME_TIMEOUT_MS
constexpr uint32_t INTER_FRAME_TIMEOUT_MS
Timeout between frames (in milliseconds).
Definition: modbus.h:61
uuid::modbus::EXCEPTION
@ EXCEPTION
Definition: modbus.h:104
uuid::modbus::RegisterRequest::RegisterRequest
RegisterRequest(uint16_t device, uint8_t function_code, uint16_t timeout_ms, uint16_t address, uint16_t data, const std::shared_ptr< Response > &response)
Create a new register request message (not directly useful).
Definition: register_functions.cpp:99
uuid::modbus::RegisterWriteResponse::parse
ResponseStatus parse(frame_buffer_t &frame, uint16_t len) override
Parse a message frame buffer and store the outcome in this response.
Definition: register_functions.cpp:136
uuid::modbus::DeviceAddressType
DeviceAddressType
Device address types.
Definition: modbus.h:76
uuid::modbus::RegisterDataResponse::data_
std::vector< uint16_t > data_
Definition: modbus.h:269
uuid::modbus::SerialClient::receive
void receive()
Receive a message frame and identify the end of a message frame (or timeout).
Definition: serial_client.cpp:157
uuid::modbus::SerialClient::last_tx_ms_
uint32_t last_tx_ms_
Definition: modbus.h:660
uuid::modbus::Request::device_
const uint16_t device_
Definition: modbus.h:405
uuid::modbus::SerialClient::serial_
::HardwareSerial & serial_
Definition: modbus.h:647
uuid::modbus::ExceptionStatusResponse
Exception status response message.
Definition: modbus.h:315
uuid::modbus::Response::exception_code
uint8_t exception_code() const
Get the exception code from the device response.
Definition: modbus.h:194
uuid::modbus::Request::device
uint16_t device() const
Get the destination device address.
Definition: modbus.h:381
uuid::modbus::RegisterWriteResponse
Register write response message.
Definition: modbus.h:280
uuid::modbus::ResponseStatus
ResponseStatus
Status of response messages.
Definition: modbus.h:99
uuid::modbus::FAILURE_TOO_SHORT
@ FAILURE_TOO_SHORT
Definition: modbus.h:108
uuid::modbus::SerialClient::log_frame
void log_frame(const __FlashStringHelper *prefix)
Log the contents of the current message frame.
Definition: serial_client.cpp:245
uuid::modbus::DEFAULT_UNICAST_TIMEOUT_MS
constexpr uint16_t DEFAULT_UNICAST_TIMEOUT_MS
Definition: modbus.h:62
uuid::modbus::logger
const uuid::log::Logger logger
Definition: modbus.cpp:37
uuid::modbus::RegisterDataResponse::parse
ResponseStatus parse(frame_buffer_t &frame, uint16_t len) override
Parse a message frame buffer and store the outcome in this response.
Definition: register_functions.cpp:116
uuid::modbus::WRITE_SINGLE_REGISTER
@ WRITE_SINGLE_REGISTER
Definition: modbus.h:90
uuid::modbus::MAX_UNICAST
@ MAX_UNICAST
Definition: modbus.h:79
uuid::modbus::FAILURE_INVALID
@ FAILURE_INVALID
Definition: modbus.h:105
uuid::modbus::SerialClient::encode
void encode()
Encode the request message at the top of the queue.
Definition: serial_client.cpp:85
uuid::modbus::Request::Request
Request(uint16_t device, uint8_t function_code, uint16_t timeout_ms, const std::shared_ptr< Response > &response)
Create a new request message (not directly useful).
Definition: generic_functions.cpp:32
uuid::modbus::ExceptionStatusResponse::parse
ResponseStatus parse(frame_buffer_t &frame, uint16_t len) override
Parse a message frame buffer and store the outcome in this response.
Definition: diagnostic_functions.cpp:53
uuid::modbus::WAITING
@ WAITING
Definition: modbus.h:102
uuid::modbus::Response::check_length
bool check_length(frame_buffer_t &frame, uint16_t actual, uint16_t expected)
Check the length of the message frame is correct and log an error if it is not.
Definition: generic_functions.cpp:44
uuid::modbus::SerialClient::idle
void idle()
Receive messages while idle.
Definition: serial_client.cpp:68
uuid::modbus::RegisterDataResponse
Register data response message.
Definition: modbus.h:245
uuid::modbus::Request::timeout_ms
uint16_t timeout_ms() const
Get the timeout to wait for a response in milliseconds.
Definition: modbus.h:397
uuid::modbus::SerialClient::default_broadcast_timeout_ms_
uint16_t default_broadcast_timeout_ms_
Definition: modbus.h:650
uuid::modbus::RegisterRequest::encode
uint16_t encode(frame_buffer_t &frame) override
Encode this request and store it in a message frame buffer.
Definition: register_functions.cpp:106
uuid::modbus::SerialClient::default_unicast_timeout_ms
void default_unicast_timeout_ms(uint16_t timeout_ms)
Set the default timeout for new unicast requests.
Definition: modbus.h:506
uuid::modbus::Response::exception
bool exception() const
Determine if the request returned an exception.
Definition: modbus.h:158
uuid::modbus::RegisterRequest::address
uint16_t address() const
Get the register address.
Definition: modbus.h:454
uuid::modbus::MESSAGE_HEADER_SIZE
constexpr uint16_t MESSAGE_HEADER_SIZE
Definition: modbus.h:48
uuid::modbus::Request::function_code
uint8_t function_code() const
Get the function code of the request.
Definition: modbus.h:389
uuid::modbus::FAILURE_CRC
@ FAILURE_CRC
Definition: modbus.h:106
uuid::modbus::SerialClient::frame_pos_
uint16_t frame_pos_
Definition: modbus.h:655
uuid::modbus::SerialClient::default_unicast_timeout_ms
uint16_t default_unicast_timeout_ms() const
Get the default timeout for new unicast requests.
Definition: modbus.h:499
uuid::modbus::RegisterRequest::address_
const uint16_t address_
Definition: modbus.h:462
uuid::modbus::FAILURE_ADDRESS
@ FAILURE_ADDRESS
Definition: modbus.h:110
uuid::modbus::BROADCAST
@ BROADCAST
Definition: modbus.h:77
uuid::modbus::SerialClient::SerialClient
SerialClient(::HardwareSerial &serial)
Create a new client.
Definition: serial_client.cpp:34
uuid::modbus::TRANSMIT
@ TRANSMIT
Definition: modbus.h:101
uuid::modbus::RegisterWriteResponse::address
uint16_t address() const
Get the address from the device response, which should match the address that was requested.
Definition: modbus.h:301
uuid::modbus::RegisterWriteResponse::address_
uint16_t address_
Definition: modbus.h:304
uuid::modbus::SerialClient::read_exception_status
std::shared_ptr< const ExceptionStatusResponse > read_exception_status(uint16_t device, uint16_t timeout_ms=0)
Read exception status from a remote device.
Definition: diagnostic_functions.cpp:34
uuid::modbus::SerialClient::calc_crc
uint16_t calc_crc() const
Calculate CRC for the current frame;.
Definition: serial_client.cpp:268
uuid::modbus::SerialClient::default_broadcast_delay_ms
uint16_t default_broadcast_delay_ms() const
Get the default timeout for new broadcast requests.
Definition: modbus.h:514
uuid::modbus::Response::success
bool success() const
Determine if the request was successful.
Definition: modbus.h:150
uuid::modbus::Response::status_
ResponseStatus status_
Definition: modbus.h:233
uuid::modbus::DEFAULT_BROADCAST_TIMEOUT_MS
constexpr uint16_t DEFAULT_BROADCAST_TIMEOUT_MS
Definition: modbus.h:63
uuid::modbus::SerialClient
Serial client used to process requests.
Definition: modbus.h:474
uuid::modbus::SerialClient::read_holding_registers
std::shared_ptr< const RegisterDataResponse > read_holding_registers(uint16_t device, uint16_t address, uint16_t size, uint16_t timeout_ms=0)
Read a contiguous block of holding registers from a remote device.
Definition: register_functions.cpp:34
uuid::modbus::READ_INPUT_REGISTERS
@ READ_INPUT_REGISTERS
Definition: modbus.h:89
uuid::modbus::SerialClient::loop
void loop()
Loop function that must be called regularly to send and receive messages.
Definition: serial_client.cpp:37
uuid
Common utilities.
Definition: get_uptime_ms.cpp:28
uuid::modbus::QUEUED
@ QUEUED
Definition: modbus.h:100
uuid::modbus::Response::status
ResponseStatus status() const
Get the status of the response message.
Definition: modbus.h:175
uuid::modbus::Response::done
bool done() const
Determine if the request is complete.
Definition: modbus.h:134
uuid::modbus::SerialClient::tx_frame_size_
uint16_t tx_frame_size_
Definition: modbus.h:659
uuid::modbus::FAILURE_LENGTH
@ FAILURE_LENGTH
Definition: modbus.h:112
uuid::modbus::Request::response_
const std::shared_ptr< Response > response_
Definition: modbus.h:411
uuid::modbus::SerialClient::input
uint32_t input()
Read message frames from the serial port device.
Definition: serial_client.cpp:127
uuid::modbus::SerialClient::transmit
void transmit()
Transmit the current message frame on the serial port device.
Definition: serial_client.cpp:107
uuid::modbus::SerialClient::idle_frame_
bool idle_frame_
Definition: modbus.h:652
uuid::modbus::SerialClient::default_broadcast_delay_ms
void default_broadcast_delay_ms(uint16_t timeout_ms)
Set the default timeout for new broadcast requests.
Definition: modbus.h:521
uuid::modbus::MESSAGE_CRC_SIZE
constexpr uint16_t MESSAGE_CRC_SIZE
Definition: modbus.h:49
uuid::modbus::FAILURE_TOO_LONG
@ FAILURE_TOO_LONG
Definition: modbus.h:109
uuid::modbus::Request::response
Response & response() const
Get the response object.
Definition: modbus.h:405
uuid::modbus::FunctionCode
FunctionCode
Function codes.
Definition: modbus.h:87
uuid::modbus::SerialClient::read_input_registers
std::shared_ptr< const RegisterDataResponse > read_input_registers(uint16_t device, uint16_t address, uint16_t size, uint16_t timeout_ms=0)
Read a contiguous block of input registers from a remote device.
Definition: register_functions.cpp:55
uuid::modbus::Response::failed
bool failed() const
Determine if the request failed for a reason other than an exception.
Definition: modbus.h:167
uuid::modbus::SerialClient::complete
void complete()
Finish current request and populate response.
Definition: serial_client.cpp:178
uuid::modbus::RegisterRequest::data
uint16_t data() const
Get the register size or value.
Definition: modbus.h:462
uuid::modbus::Response::parse
virtual ResponseStatus parse(frame_buffer_t &frame, uint16_t len)=0
Parse a message frame buffer and store the outcome in this response.
uuid::modbus::READ_HOLDING_REGISTERS
@ READ_HOLDING_REGISTERS
Definition: modbus.h:88
uuid::modbus::ExceptionStatusResponse::data
uint8_t data() const
Get the output data from the device response.
Definition: modbus.h:335
uuid::modbus::Response
Response message.
Definition: modbus.h:124
uuid::modbus::RegisterRequest
Request message for register functions.
Definition: modbus.h:422
uuid::modbus::RegisterDataResponse::data
const std::vector< uint16_t > & data() const
Data from the device response, which may be fewer or more register values than requested.
Definition: modbus.h:256
uuid::modbus::SerialClient::requests_
std::deque< std::unique_ptr< Request > > requests_
Definition: modbus.h:648
uuid::modbus::READ_EXCEPTION_STATUS
@ READ_EXCEPTION_STATUS
Definition: modbus.h:91
uuid::modbus::Response::exception_code
void exception_code(uint8_t exception_code)
Set the exception code from the device response.
Definition: modbus.h:205
uuid::modbus::SerialClient::default_unicast_timeout_ms_
uint16_t default_unicast_timeout_ms_
Definition: modbus.h:649
uuid::modbus::FAILURE_UNEXPECTED
@ FAILURE_UNEXPECTED
Definition: modbus.h:113
uuid::modbus::Response::exception_code_
uint8_t exception_code_
Definition: modbus.h:234
uuid::modbus::SerialClient::frame_
frame_buffer_t frame_
Definition: modbus.h:654