9static const char *
const TAG =
"modbus";
12static constexpr size_t MODBUS_MAX_LOG_BYTES = 64;
15static constexpr uint32_t MODBUS_BITS_PER_CHAR = 11;
17static constexpr uint32_t MS_PER_SEC = 1000;
32 static constexpr uint16_t DEFAULT_LONG_RX_BUFFER_DELAY_MS = 50;
37 : DEFAULT_LONG_RX_BUFFER_DELAY_MS;
47 const uint16_t timeout = std::max(
49 (uint16_t) (this->
rx_buffer_.size() >= this->parent_->get_rx_full_threshold() ? this->long_rx_buffer_delay_ms_
63 (this->
rx_buffer_.empty() || this->rx_buffer_[0] != this->waiting_for_response_)) {
64 ESP_LOGW(TAG,
"Stop waiting for response from %" PRIu8
" %" PRIu32
"ms after last send",
97 size_t to_read = std::min(avail,
sizeof(buf));
102 for (
size_t i = 0; i < to_read; i++) {
104 ESP_LOGV(TAG,
"Received first byte %" PRIu8
" (0X%x) %" PRIu32
"ms after last send", buf[i], buf[i],
107 ESP_LOGVV(TAG,
"Received byte %" PRIu8
" (0X%x) %" PRIu32
"ms after last send", buf[i], buf[i],
137 uint8_t function_code =
raw[1];
139 uint8_t data_len =
raw[2];
140 uint8_t data_offset = 3;
156 uint16_t computed_crc =
crc16(
raw, data_offset + data_len);
157 uint16_t remote_crc = uint16_t(
raw[data_offset + data_len]) | (uint16_t(
raw[data_offset + data_len + 1]) << 8);
159 if (computed_crc != remote_crc)
162 ESP_LOGD(TAG,
"User-defined function %02X found", function_code);
180 data_len = 2 + 2 + 1 +
raw[6];
201 if (at < data_offset + data_len)
205 if (at == data_offset + data_len)
209 uint16_t computed_crc =
crc16(
raw, data_offset + data_len);
210 uint16_t remote_crc = uint16_t(
raw[data_offset + data_len]) | (uint16_t(
raw[data_offset + data_len + 1]) << 8);
211 if (computed_crc != remote_crc) {
213 ESP_LOGD(TAG,
"CRC check failed %" PRIu32
"ms after last send; ignoring",
millis() - this->
last_send_);
214#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
217 ESP_LOGVV(TAG,
" (%02X != %02X) %s", computed_crc, remote_crc,
220 ESP_LOGW(TAG,
"CRC check failed %" PRIu32
"ms after last send",
millis() - this->
last_send_);
221#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
224 ESP_LOGVV(TAG,
" (%02X != %02X) %s", computed_crc, remote_crc,
230 std::vector<uint8_t> data(this->
rx_buffer_.begin() + data_offset, this->rx_buffer_.begin() + data_offset + data_len);
232 for (
auto *device : this->
devices_) {
233 if (device->address_ ==
address) {
238 device->on_modbus_read_registers(function_code, uint16_t(data[1]) | (uint16_t(data[0]) << 8),
239 uint16_t(data[3]) | (uint16_t(data[2]) << 8));
242 device->on_modbus_write_registers(function_code, data);
247 uint8_t exception =
raw[2];
249 "Error function code: 0x%X exception: %" PRIu8
", address: %" PRIu8
", %" PRIu32
250 "ms after last send",
256 ESP_LOGD(TAG,
"Ignoring error - not expecting a response from %" PRIu8
"",
address);
260 device->on_modbus_data(data);
263 ESP_LOGW(TAG,
"Ignoring response - not expecting a response from %" PRIu8
", %" PRIu32
"ms after last send",
272 ESP_LOGW(TAG,
"Got frame from unknown address %" PRIu8
", %" PRIu32
"ms after last send",
address,
308#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
316 ESP_LOGV(TAG,
"Write queue contains %" PRIu32
" items.", this->
tx_buffer_.size());
323 " Send Wait Time: %d ms\n"
324 " Turnaround Time: %d ms\n"
325 " Frame Delay: %d ms\n"
326 " Long Rx Buffer Delay: %d ms\n"
337void Modbus::send(uint8_t
address, uint8_t function_code, uint16_t start_address, uint16_t number_of_entities,
338 uint8_t payload_len,
const uint8_t *payload) {
339 static const size_t MAX_VALUES = 128;
344 ESP_LOGE(TAG,
"send too many values %d max=%zu", number_of_entities, MAX_VALUES);
348 uint8_t data[MAX_FRAME_SIZE];
352 data[
pos++] = function_code;
354 data[
pos++] = start_address >> 8;
355 data[
pos++] = start_address >> 0;
358 data[
pos++] = number_of_entities >> 8;
359 data[
pos++] = number_of_entities >> 0;
363 if (payload !=
nullptr) {
366 data[
pos++] = payload_len;
370 if (payload_len +
pos + 2 > MAX_FRAME_SIZE) {
371 ESP_LOGE(TAG,
"Payload too large to send: %d bytes", payload_len);
374 for (
int i = 0; i < payload_len; i++) {
375 data[
pos++] = payload[i];
385 if (payload.empty()) {
389 if (payload.size() + 2 > MAX_FRAME_SIZE) {
390 ESP_LOGE(TAG,
"Attempted to send frame larger than max frame size of %d bytes", MAX_FRAME_SIZE);
394 uint8_t data[MAX_FRAME_SIZE];
396 std::memcpy(data, payload.data(), payload.size());
404 if (this->
tx_buffer_.size() < MODBUS_TX_BUFFER_SIZE) {
407#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
418 ESP_LOGW(TAG,
"Clearing buffer of %" PRIu32
" bytes - %s %" PRIu32
"ms after last send", at, LOG_STR_ARG(reason),
421 ESP_LOGV(TAG,
"Clearing buffer of %" PRIu32
" bytes - %s %" PRIu32
"ms after last send", at, LOG_STR_ARG(reason),
virtual void digital_write(bool value)=0
std::deque< ModbusDeviceCommand > tx_buffer_
bool parse_modbus_byte_(uint8_t byte)
void send_raw(const std::vector< uint8_t > &payload)
uint8_t waiting_for_response_
uint32_t last_modbus_byte_
GPIOPin * flow_control_pin_
std::vector< ModbusDevice * > devices_
uint32_t last_send_tx_offset_
void queue_raw_(const uint8_t *data, uint16_t len)
float get_setup_priority() const override
uint16_t long_rx_buffer_delay_ms_
void dump_config() override
void receive_and_parse_modbus_bytes_()
std::vector< uint8_t > rx_buffer_
void clear_rx_buffer_(const LogString *reason, bool warn=false)
void send(uint8_t address, uint8_t function_code, uint16_t start_address, uint16_t number_of_entities, uint8_t payload_len=0, const uint8_t *payload=nullptr)
uint16_t turnaround_delay_ms_
uint32_t get_baud_rate() const
static constexpr size_t RX_FULL_THRESHOLD_UNSET
size_t get_rx_full_threshold()
optional< std::array< uint8_t, N > > read_array()
void write_array(const uint8_t *data, size_t len)
const uint8_t FUNCTION_CODE_MASK
const uint8_t FUNCTION_CODE_EXCEPTION_MASK
@ WRITE_MULTIPLE_REGISTERS
const uint8_t FUNCTION_CODE_USER_DEFINED_SPACE_2_INIT
const uint8_t FUNCTION_CODE_USER_DEFINED_SPACE_1_INIT
Modbus definitions from specs: https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3....
const uint8_t FUNCTION_CODE_USER_DEFINED_SPACE_2_END
const uint8_t FUNCTION_CODE_USER_DEFINED_SPACE_1_END
constexpr float BUS
For communication buses like i2c/spi.
Providing packet encoding functions for exchanging data with a remote host.
uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t reverse_poly, bool refin, bool refout)
Calculate a CRC-16 checksum of data with size len.
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".
uint32_t IRAM_ATTR HOT millis()
std::unique_ptr< uint8_t[]> data