8static const char *
const TAG =
"modbus";
11static constexpr size_t MODBUS_MAX_LOG_BYTES = 64;
14static constexpr uint32_t MODBUS_BITS_PER_CHAR = 11;
16static constexpr uint32_t MS_PER_SEC = 1000;
31 static constexpr uint16_t DEFAULT_LONG_RX_BUFFER_DELAY_MS = 50;
36 : DEFAULT_LONG_RX_BUFFER_DELAY_MS;
46 const uint16_t timeout = std::max(
48 (uint16_t) (this->
rx_buffer_.size() >= this->parent_->get_rx_full_threshold() ? this->long_rx_buffer_delay_ms_
62 (this->
rx_buffer_.empty() || this->rx_buffer_[0] != this->waiting_for_response_)) {
63 ESP_LOGW(TAG,
"Stop waiting for response from %" PRIu8
" %" PRIu32
"ms after last send",
96 size_t to_read = std::min(avail,
sizeof(buf));
101 for (
size_t i = 0; i < to_read; i++) {
103 ESP_LOGV(TAG,
"Received first byte %" PRIu8
" (0X%x) %" PRIu32
"ms after last send", buf[i], buf[i],
106 ESP_LOGVV(TAG,
"Received byte %" PRIu8
" (0X%x) %" PRIu32
"ms after last send", buf[i], buf[i],
136 uint8_t function_code =
raw[1];
138 uint8_t data_len =
raw[2];
139 uint8_t data_offset = 3;
155 uint16_t computed_crc =
crc16(
raw, data_offset + data_len);
156 uint16_t remote_crc = uint16_t(
raw[data_offset + data_len]) | (uint16_t(
raw[data_offset + data_len + 1]) << 8);
158 if (computed_crc != remote_crc)
161 ESP_LOGD(TAG,
"User-defined function %02X found", function_code);
179 data_len = 2 + 2 + 1 +
raw[6];
200 if (at < data_offset + data_len)
204 if (at == data_offset + data_len)
208 uint16_t computed_crc =
crc16(
raw, data_offset + data_len);
209 uint16_t remote_crc = uint16_t(
raw[data_offset + data_len]) | (uint16_t(
raw[data_offset + data_len + 1]) << 8);
210 if (computed_crc != remote_crc) {
212 ESP_LOGD(TAG,
"CRC check failed %" PRIu32
"ms after last send; ignoring",
millis() - this->
last_send_);
213#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
216 ESP_LOGVV(TAG,
" (%02X != %02X) %s", computed_crc, remote_crc,
219 ESP_LOGW(TAG,
"CRC check failed %" PRIu32
"ms after last send",
millis() - this->
last_send_);
220#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
223 ESP_LOGVV(TAG,
" (%02X != %02X) %s", computed_crc, remote_crc,
229 std::vector<uint8_t> data(this->
rx_buffer_.begin() + data_offset, this->rx_buffer_.begin() + data_offset + data_len);
231 for (
auto *device : this->
devices_) {
232 if (device->address_ ==
address) {
237 device->on_modbus_read_registers(function_code, uint16_t(data[1]) | (uint16_t(data[0]) << 8),
238 uint16_t(data[3]) | (uint16_t(data[2]) << 8));
241 device->on_modbus_write_registers(function_code, data);
246 uint8_t exception =
raw[2];
248 "Error function code: 0x%X exception: %" PRIu8
", address: %" PRIu8
", %" PRIu32
249 "ms after last send",
255 ESP_LOGD(TAG,
"Ignoring error - not expecting a response from %" PRIu8
"",
address);
259 device->on_modbus_data(data);
262 ESP_LOGW(TAG,
"Ignoring response - not expecting a response from %" PRIu8
", %" PRIu32
"ms after last send",
271 ESP_LOGW(TAG,
"Got frame from unknown address %" PRIu8
", %" PRIu32
"ms after last send",
address,
307#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
315 ESP_LOGV(TAG,
"Write queue contains %zu items.", this->
tx_buffer_.size());
322 " Send Wait Time: %d ms\n"
323 " Turnaround Time: %d ms\n"
324 " Frame Delay: %d ms\n"
325 " Long Rx Buffer Delay: %d ms\n"
336void Modbus::send(uint8_t
address, uint8_t function_code, uint16_t start_address, uint16_t number_of_entities,
337 uint8_t payload_len,
const uint8_t *payload) {
338 static const size_t MAX_VALUES = 128;
343 ESP_LOGE(TAG,
"send too many values %d max=%zu", number_of_entities, MAX_VALUES);
347 uint8_t data[MAX_FRAME_SIZE];
351 data[
pos++] = function_code;
353 data[
pos++] = start_address >> 8;
354 data[
pos++] = start_address >> 0;
357 data[
pos++] = number_of_entities >> 8;
358 data[
pos++] = number_of_entities >> 0;
362 if (payload !=
nullptr) {
365 data[
pos++] = payload_len;
369 if (payload_len +
pos + 2 > MAX_FRAME_SIZE) {
370 ESP_LOGE(TAG,
"Payload too large to send: %d bytes", payload_len);
373 for (
int i = 0; i < payload_len; i++) {
374 data[
pos++] = payload[i];
384 if (payload.empty()) {
388 if (payload.size() + 2 > MAX_FRAME_SIZE) {
389 ESP_LOGE(TAG,
"Attempted to send frame larger than max frame size of %d bytes", MAX_FRAME_SIZE);
393 uint8_t data[MAX_FRAME_SIZE];
395 std::memcpy(data, payload.data(), payload.size());
403 if (this->
tx_buffer_.size() < MODBUS_TX_BUFFER_SIZE) {
406#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
417 ESP_LOGW(TAG,
"Clearing buffer of %zu bytes - %s %" PRIu32
"ms after last send", at, LOG_STR_ARG(reason),
420 ESP_LOGV(TAG,
"Clearing buffer of %zu 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.
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