[uart] Add packet_transport platform (#8214)

Co-authored-by: Faidon Liambotis <paravoid@debian.org>
Co-authored-by: clydeps <U5yx99dok9>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Clyde Stubbs 2025-05-05 14:15:46 +10:00 committed by GitHub
parent ad99d7fb45
commit e7a2b395fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 153 additions and 0 deletions

View File

@ -468,6 +468,7 @@ esphome/components/tuya/switch/* @jesserockz
esphome/components/tuya/text_sensor/* @dentra
esphome/components/uart/* @esphome/core
esphome/components/uart/button/* @ssieb
esphome/components/uart/packet_transport/* @clydebarrow
esphome/components/udp/* @clydebarrow
esphome/components/ufire_ec/* @pvizeli
esphome/components/ufire_ise/* @pvizeli

View File

@ -0,0 +1,20 @@
from esphome.components.packet_transport import (
PacketTransport,
new_packet_transport,
transport_schema,
)
from esphome.cpp_types import PollingComponent
from .. import UART_DEVICE_SCHEMA, register_uart_device, uart_ns
CODEOWNERS = ["@clydebarrow"]
DEPENDENCIES = ["uart"]
UARTTransport = uart_ns.class_("UARTTransport", PacketTransport, PollingComponent)
CONFIG_SCHEMA = transport_schema(UARTTransport).extend(UART_DEVICE_SCHEMA)
async def to_code(config):
var, _ = await new_packet_transport(config)
await register_uart_device(var, config)

View File

@ -0,0 +1,88 @@
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "uart_transport.h"
namespace esphome {
namespace uart {
static const char *const TAG = "uart_transport";
void UARTTransport::loop() {
PacketTransport::loop();
while (this->parent_->available()) {
uint8_t byte;
if (!this->parent_->read_byte(&byte)) {
ESP_LOGW(TAG, "Failed to read byte from UART");
return;
}
if (byte == FLAG_BYTE) {
if (this->rx_started_ && this->receive_buffer_.size() > 6) {
auto len = this->receive_buffer_.size();
auto crc = crc16(this->receive_buffer_.data(), len - 2);
if (crc != (this->receive_buffer_[len - 2] | (this->receive_buffer_[len - 1] << 8))) {
ESP_LOGD(TAG, "CRC mismatch, discarding packet");
this->rx_started_ = false;
this->receive_buffer_.clear();
continue;
}
this->receive_buffer_.resize(len - 2);
this->process_(this->receive_buffer_);
this->rx_started_ = false;
} else {
this->rx_started_ = true;
}
this->receive_buffer_.clear();
this->rx_control_ = false;
continue;
}
if (!this->rx_started_)
continue;
if (byte == CONTROL_BYTE) {
this->rx_control_ = true;
continue;
}
if (this->rx_control_) {
byte ^= 0x20;
this->rx_control_ = false;
}
if (this->receive_buffer_.size() == MAX_PACKET_SIZE) {
ESP_LOGD(TAG, "Packet too large, discarding");
this->rx_started_ = false;
this->receive_buffer_.clear();
continue;
}
this->receive_buffer_.push_back(byte);
}
}
void UARTTransport::update() {
this->updated_ = true;
this->resend_data_ = true;
PacketTransport::update();
}
/**
* Write a byte to the UART bus. If the byte is a flag or control byte, it will be escaped.
* @param byte The byte to write.
*/
void UARTTransport::write_byte_(uint8_t byte) const {
if (byte == FLAG_BYTE || byte == CONTROL_BYTE) {
this->parent_->write_byte(CONTROL_BYTE);
byte ^= 0x20;
}
this->parent_->write_byte(byte);
}
void UARTTransport::send_packet(std::vector<uint8_t> &buf) const {
this->parent_->write_byte(FLAG_BYTE);
for (uint8_t byte : buf) {
this->write_byte_(byte);
}
auto crc = crc16(buf.data(), buf.size());
this->write_byte_(crc & 0xFF);
this->write_byte_(crc >> 8);
this->parent_->write_byte(FLAG_BYTE);
}
} // namespace uart
} // namespace esphome

View File

@ -0,0 +1,41 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/packet_transport/packet_transport.h"
#include <vector>
#include "../uart.h"
namespace esphome {
namespace uart {
/**
* A transport protocol for sending and receiving packets over a UART connection.
* The protocol is based on Asynchronous HDLC framing. (https://en.wikipedia.org/wiki/High-Level_Data_Link_Control)
* There are two special bytes: FLAG_BYTE and CONTROL_BYTE.
* A 16-bit CRC is appended to the packet, then
* the protocol wraps the resulting data between FLAG_BYTEs.
* Any occurrence of FLAG_BYTE or CONTROL_BYTE in the data is escaped by emitting CONTROL_BYTE followed by the byte
* XORed with 0x20.
*/
static const uint16_t MAX_PACKET_SIZE = 508;
static const uint8_t FLAG_BYTE = 0x7E;
static const uint8_t CONTROL_BYTE = 0x7D;
class UARTTransport : public packet_transport::PacketTransport, public UARTDevice {
public:
void loop() override;
void update() override;
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
protected:
void write_byte_(uint8_t byte) const;
void send_packet(std::vector<uint8_t> &buf) const override;
bool should_send() override { return true; };
size_t get_max_packet_size() override { return MAX_PACKET_SIZE; }
std::vector<uint8_t> receive_buffer_{};
bool rx_started_{};
bool rx_control_{};
};
} // namespace uart
} // namespace esphome

View File

@ -13,3 +13,6 @@ uart:
rx_buffer_size: 512
parity: EVEN
stop_bits: 2
packet_transport:
- platform: uart