My Project
register_functions.cpp
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 #include <uuid/modbus.h>
20 
21 #include <Arduino.h>
22 
23 #include <algorithm>
24 #include <cstdarg>
25 #include <cstdint>
26 #include <memory>
27 
28 #include <make_unique.cpp>
29 
30 namespace uuid {
31 
32 namespace modbus {
33 
34 std::shared_ptr<const RegisterDataResponse> SerialClient::read_holding_registers(
35  uint16_t device, uint16_t address, uint16_t size, uint16_t timeout_ms) {
36  auto response = std::make_shared<RegisterDataResponse>();
37 
38  if (device < DeviceAddressType::MIN_UNICAST
39  || device > DeviceAddressType::MAX_UNICAST
40  || size < 1 || size > 0x007D) {
41  response->status(ResponseStatus::FAILURE_INVALID);
42  } else {
43  if (timeout_ms == 0) {
44  timeout_ms = default_unicast_timeout_ms_;
45  }
46 
47  requests_.push_back(std::make_unique<RegisterRequest>(device,
48  FunctionCode::READ_HOLDING_REGISTERS, timeout_ms, address, size,
49  response));
50  }
51 
52  return response;
53 }
54 
55 std::shared_ptr<const RegisterDataResponse> SerialClient::read_input_registers(
56  uint16_t device, uint16_t address, uint16_t size, uint16_t timeout_ms) {
57  auto response = std::make_shared<RegisterDataResponse>();
58 
59  if (device < DeviceAddressType::MIN_UNICAST
60  || device > DeviceAddressType::MAX_UNICAST
61  || size < 1 || size > 0x007D) {
62  response->status(ResponseStatus::FAILURE_INVALID);
63  } else {
64  if (timeout_ms == 0) {
65  timeout_ms = default_unicast_timeout_ms_;
66  }
67 
68  requests_.push_back(std::make_unique<RegisterRequest>(device,
69  FunctionCode::READ_INPUT_REGISTERS, timeout_ms, address, size,
70  response));
71  }
72 
73  return response;
74 }
75 
76 std::shared_ptr<const RegisterWriteResponse> SerialClient::write_holding_register(
77  uint16_t device, uint16_t address, uint16_t value, uint16_t timeout_ms) {
78  auto response = std::make_shared<RegisterWriteResponse>();
79 
80  if (device > DeviceAddressType::MAX_UNICAST) {
81  response->status(ResponseStatus::FAILURE_INVALID);
82  } else {
83  if (timeout_ms == 0) {
84  if (device == DeviceAddressType::BROADCAST) {
85  timeout_ms = default_broadcast_timeout_ms_;
86  } else {
87  timeout_ms = default_unicast_timeout_ms_;
88  }
89  }
90 
91  requests_.push_back(std::make_unique<RegisterRequest>(device,
92  FunctionCode::WRITE_SINGLE_REGISTER, timeout_ms, address, value,
93  response));
94  }
95 
96  return response;
97 }
98 
99 RegisterRequest::RegisterRequest(uint16_t device, uint8_t function_code,
100  uint16_t timeout_ms, uint16_t address, uint16_t data,
101  const std::shared_ptr<Response> &response)
102  : Request(device, function_code, timeout_ms, response),
103  address_(address), data_(data) {
104 }
105 
107  frame[0] = device();
108  frame[1] = function_code();
109  frame[2] = address() >> 8;
110  frame[3] = address() & 0xFF;
111  frame[4] = data() >> 8;
112  frame[5] = data() & 0xFF;
113  return 6;
114 }
115 
117  if (len < 3) {
118  logger.err(F("Incomplete message for function %02X from device %u, expected 3+ received %u"),
119  frame[1], frame[0], len);
120  return ResponseStatus::FAILURE_LENGTH;
121  } else if (!check_length(frame, len, 3 + frame[2])) {
122  return ResponseStatus::FAILURE_LENGTH;
123  } else if (frame[2] & 1) {
124  logger.err(F("Invalid message for function %02X from device %u, byte count %u is not a multiple of 2"),
125  frame[1], frame[0], frame[2]);
126  return ResponseStatus::FAILURE_LENGTH;
127  }
128 
129  for (uint16_t i = 0; i < frame[2]; i += 2) {
130  data_.emplace_back((frame[3 + i] << 8) | frame[4 + i]);
131  }
132 
133  return ResponseStatus::SUCCESS;
134 }
135 
137  if (!check_length(frame, len, 6)) {
138  return ResponseStatus::FAILURE_LENGTH;
139  }
140 
141  address_ = (frame[2] << 8) | frame[3];
142  data_.emplace_back((frame[4] << 8) | frame[5]);
143 
144  return ResponseStatus::SUCCESS;
145 }
146 
147 } // namespace modbus
148 
149 } // namespace uuid
uuid::modbus::frame_buffer_t
std::array< uint8_t, MAX_MESSAGE_SIZE+1 > frame_buffer_t
Definition: modbus.h:67
uuid::log::Logger::err
void err(const char *format,...) const
Log a message at level Level::ERR.
Definition: log.cpp:170
uuid::modbus::Request
Request message.
Definition: modbus.h:349
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::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::RegisterDataResponse::data_
std::vector< uint16_t > data_
Definition: modbus.h:269
uuid::modbus::Request::device
uint16_t device() const
Get the destination device address.
Definition: modbus.h:381
uuid::modbus::ResponseStatus
ResponseStatus
Status of response messages.
Definition: modbus.h:99
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::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::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::RegisterRequest::address
uint16_t address() const
Get the register address.
Definition: modbus.h:454
uuid::modbus::Request::function_code
uint8_t function_code() const
Get the function code of the request.
Definition: modbus.h:389
uuid::modbus::RegisterWriteResponse::address_
uint16_t address_
Definition: modbus.h:304
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
Common utilities.
Definition: get_uptime_ms.cpp:28
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::RegisterRequest::data
uint16_t data() const
Get the register size or value.
Definition: modbus.h:462
uuid::modbus::SerialClient::requests_
std::deque< std::unique_ptr< Request > > requests_
Definition: modbus.h:648
uuid::modbus::SerialClient::default_unicast_timeout_ms_
uint16_t default_unicast_timeout_ms_
Definition: modbus.h:649