跳转到内容

UART 总线

UART 是一种用于许多设备的常见串行协议。例如,在向你的 ESP 上传二进制文件时 你可能已经使用 UART 来访问芯片。UART(或对 Arduino 而言通常也称为 Serial)通常 由 2 个引脚组成:

  • TX:此线路用于向对端的设备发送数据。
  • RX:此线路用于从对端的设备接收数据。

请注意,这两个引脚的命名取决于所选的角度,可能会有歧义。例如, 尽管 ESP 可能在引脚 A 上发送(TX)并在引脚 B 上接收(RX)数据,但从其他设备的 角度来看,这两个引脚是交换的(即它在引脚 B 上发送并在引脚 A 上接收)。因此,如果 立即不起作用,你可能需要将两个引脚交换后再试。

此外,每个 UART 总线可以以不同的速度(波特率)运行,因此 ESPHome 需要知道以什么速度 接收/发送数据,这可以通过 baud_rate 选项指定。两种常见的波特率是 9600 和 115200。

在某些情况下只有 TX 或 RX 存在,因为对端的设备只接受数据或只发送数据。

UART 组件可作为 数据包传输组件 的平台,使传感器数据能够通过 UART 总线从一个 ESPHome 节点直接发送到另一个节点。使用 RS485 时,这可以 以多点配置运行。

NOTE

在 ESP32 上,此组件使用硬件 UART 单元,因此非常精确。在 ESP8266 上, ESPHome 必须使用软件实现,因为没有其他硬件 UART 单元可用(除用于 日志记录的单元之外)。因此,ESP8266 上的 UART 数据可能会出现偶尔的数据错误,特别是在 较高波特率下。

NOTE

从 ESPHome 2021.8 开始,ESP8266SoftwareSerial UART 的 write_byte 函数的校验位已修复为正确 反映发送的数据。如果你使用软件 UART 并且有设备 明确检查校验位,这可能会导致意外问题。很可能你需要翻转 YAML 中的 parity 标志。

NOTE

主机平台的 UART 实现不使用 TX 和 RX 引脚,而使用端口名称。

# 示例配置条目
uart:
tx_pin: GPIOXX
rx_pin: GPIOXX
baud_rate: 9600
  • baud_rate (必选, int): UART 总线的波特率。

  • tx_pin (可选, Pin): ESP 视角下用于发送数据的引脚。使用完整的引脚 模式并将 inverted: true 以反相逻辑电平。主机平台不支持。

  • rx_pin (可选, Pin): ESP 视角下用于接收数据的引脚。使用完整的引脚 模式并将 inverted: true 以反相逻辑电平。主机平台不支持。

  • flow_control_pin (可选, Pin): 仅限 ESP32。用于硬件 RS485 流控制的引脚。 使用此设置将启用半双工模式。使用完整的引脚 模式并将 inverted: true 以反相逻辑电平。

  • port (可选, string): 仅主机平台。要使用的端口的 Unix 风格名称。

  • rx_buffer_size (可选, int): 用于接收 UART 消息的缓冲区大小。如果你使用需要 从 UART 读取大量负载的集成,可以增加此值。默认为 256。

  • rx_full_threshold (可选, int): 仅限 ESP32。收到此数量的字节后,数据将可供处理。 默认值在编译时计算,大约为 10 毫秒(9600 波特约 8 字节,115200 波特约 114 字节)。

  • rx_timeout (可选, int): 仅限 ESP32。此值指定用于确定超时持续时间的字节数。超时的持续时间基于接收此数量字节所需的时间。换言之,对于给定值和波特率,波特率加倍将使超时持续时间减半。超时过去后,数据将可供处理。默认为 2。

  • flush_timeout (可选, Time): 仅限 ESP32。调用 flush() 时等待 TX FIFO 排空的最大时间。如果 TX FIFO 在此时间内未排空,flush() 返回 TIMEOUT 结果。未设置时,flush() 将无限期等待。

  • data_bits (可选, int): UART 总线上使用的数据位数。选项:5 到 8。默认为 8。

  • parity (可选): UART 总线上使用的校验方式。选项:NONE、EVEN、ODD。默认为 NONE。

  • stop_bits (可选, int): 要发送的停止位数。选项:1、2。默认为 1。

  • id (可选, ID): 如果你需要多个 UART 中心,可手动指定此 UART 中心的 ID。

  • debug (可选, mapping): 用于调试 UART 中心上通信的选项,参见 调试。

在可能的情况下,ESPHome 将使用 ESP8266 上的硬件 UART 单元进行快速精确的通信。 当硬件 UART 都被占用时,ESPHome 将回退到软件实现,在较高波特率下可能不精确。

UART0(默认情况下)由 logger 组件 使用,使用 tx_pin: GPIO1 和 rx_pin: GPIO3。如果你配置了一个与这些引脚重叠的 UART,你可以与 logger 共享硬件并使其他的可用。如果你已将 logger 配置为使用不同的硬件 UART,则 用于硬件共享的引脚会相应改变。

原始的 ESP32 有三个 UART。ESP32 变体(ESP32-C3、ESP32-S2 等)可能有不同数量的 UART (通常为 2 到 6 个)。任何一对 GPIO 引脚都可以使用,只要它们支持正确的输出/输入模式即可。

ESP8266 有两个 UART;其中第二个仅 TX。只有有限的一组引脚可以使用。UART0 可 以使用 tx_pin: GPIO1 和 rx_pin: GPIO3,或使用 tx_pin: GPIO15 和 rx_pin: GPIO13。UART1 必须 使用 tx_pin: GPIO2。任何其他引脚组合都将导致使用软件 UART。

NOTE

软件 UART 仅在 ESP8266 上可用。ESP32 和变体上不可用。

此 Action 向给定的 UART 总线发送定义的 UART 信号。

on_...:
- uart.write: 'Hello World'
# 对于转义字符,必须使用双引号!
- uart.write: "Hello World\r\n"
# 原始数据
- uart.write: [0x00, 0x20, 0x42]
# 模板化,返回类型为 std::vector<uint8_t>
- uart.write: !lambda
return {0x00, 0x20, 0x42};
# 如果需要指定 uart id
- uart.write:
id: my_second_uart
data: 'other data'

如果你需要了解通过 UART 总线发送和/或接收的通信情况,可以使用 调试功能。

# 示例配置条目
uart:
baud_rate: 115200
debug:
direction: BOTH
dummy_receiver: false
debug_prefix: "[MODBUS] "
after:
delimiter: "\n"
sequence:
- lambda: UARTDebug::log_string(direction, bytes, debug_prefix);
# 最小配置示例,默认记录十六进制字符串
uart:
baud_rate: 9600
debug:
  • direction (可选, enum): 要调试的通信方向,可选:RX(接收,传入)、 TX(发送,传出)或 BOTH。默认为 BOTH。

  • debug_prefix (可选, string): 添加到每个调试日志行前面的字符串,用于此 UART 总线。 当多个 UART 总线启用了调试时,有助于在日志中区分它们的输出。默认为 ""。

  • dummy_receiver (可选, boolean): 是否启用 dummy receiver 功能。调试器 只会累积 UART 设备组件实际读取或发送的字节。此功能 在你想要调试所有传入通信、而尚未为 UART 总线配置 UART 设备组件 时很有用。这尤其适合开发者。通常你希望将此 选项保持禁用。默认为 false。

  • after (可选, mapping): 调试器累积通信字节。此选项定义何时 触发发布累积的字节。可能的选项有:

    • bytes (可选, int): 累积指定数量的字节后触发。默认为 150。

    • timeout (可选, Time): 在指定超时期间没有看到任何通信后触发, 前提是已累积一个或多个字节。默认为 100ms。

    • delimiter (可选, string 或字节列表): 检测到通信中出现指定的字节序列后触发。

  • sequence (可选, Action): 用于发布调试数据的动作。 默认为以十六进制格式记录字节的动作。这些动作可以使用以下变量:

    • direction: uart::UART_DIRECTION_RX 或 uart::UART_DIRECTION_TX
    • bytes: std::vector<uint8_t>,包含累积的字节
    • debug_prefix: StringRef,包含配置的标题(未设置时为字符串 "")

    日志记录辅助函数

    提供了辅助函数,使以各种格式记录调试数据变得容易:

    • UARTDebug::log_hex(direction, bytes, char separator, debug_prefix) 将字节作为十六进制值记录,用提供的 分隔字符分隔。

    • UARTDebug::log_string(direction, bytes, debug_prefix) 将字节作为字符串值记录,转义不可打印的字符。

    • UARTDebug::log_int(direction, bytes, char separator, debug_prefix) 将字节作为整数值记录,用提供的 分隔字符分隔。

    • UARTDebug::log_binary(direction, bytes, char separator, debug_prefix) 将字节作为 <binary> (<hex>) 值记录, 用提供的分隔字符分隔。

    所有辅助函数中的 debug_prefix 参数都是可选的(默认为空)。使用默认 sequence 时, debug_prefix 会自动传递。

    Logger 缓冲区大小

    请注意,logger 组件默认使用 512 字节的有限缓冲区大小。如果 UART 调试器日志行变得太长,你将注意到它们最终在日志输出中被截断。

    在这种情况下,确保调试器每行输出的数据更少(例如,通过将 after.bytes 选项设置为较低的值),或使用 logger 的 tx_buffer_size 选项增加 logger 缓冲区大小。

在某些场景中,你可能需要在运行时调整 UART 参数,以提高通信效率 并适应不同的运行条件。ESPHome 通过 lambda 调用来实现这一点。 以下是读取当前设置和动态修改它们的方法:

  • 读取当前设置: 使用这些只读属性访问 UART 的当前配置:
// RX 缓冲区大小
id(my_uart).get_rx_buffer_size();
// RX FIFO 满阈值
id(my_uart).get_rx_full_threshold();
// RX 超时
id(my_uart).get_rx_timeout();
// 停止位
id(my_uart).get_stop_bits();
// 数据位
id(my_uart).get_data_bits();
// 校验
id(my_uart).get_parity();
// 波特率
id(my_uart).get_baud_rate();
  • 在运行时修改设置: 你可以在运行时更改某些 UART 参数。 设置新值后,调用 load_settings()(仅限 ESP)以应用这些更改:
select:
- id: change_baud_rate
name: Baud rate
platform: template
options:
- "2400"
- "9600"
- "38400"
- "57600"
- "115200"
- "256000"
- "512000"
- "921600"
initial_option: "115200"
optimistic: true
restore_value: True
internal: false
entity_category: config
icon: mdi:swap-horizontal
set_action:
- lambda: |-
id(my_uart).flush();
uint32_t new_baud_rate = stoi(x);
ESP_LOGD("change_baud_rate", "Changing baud rate from %i to %i",id(my_uart).get_baud_rate(),
new_baud_rate);
if (id(my_uart).get_baud_rate() != new_baud_rate) {
id(my_uart).set_baud_rate(new_baud_rate);
id(my_uart).load_settings();
}

用于运行时更改的可用方法:

// 设置 TX/RX/流控制引脚
id(my_uart).set_tx_pin(InternalGPIOPin *tx_pin);
id(my_uart).set_rx_pin(InternalGPIOPin *rx_pin);
id(my_uart).set_flow_control_pin(InternalGPIOPin *flow_control_pin);
// RX 缓冲区大小
id(my_uart).set_rx_buffer_size(size_t rx_buffer_size);
// RX FIFO 满阈值(以字节为单位)
id(my_uart).set_rx_full_threshold(size_t rx_full_threshold);
// RX FIFO 满阈值(以时间单位)
id(my_uart).set_rx_full_threshold_ms(uint8_t time);
// RX 超时
id(my_uart).set_rx_timeout(size_t rx_timeout);
// 刷新超时(仅限 ESP32;0 = 无限期等待)
id(my_uart).set_flush_timeout(uint32_t flush_timeout_ms);
// 停止位
id(my_uart).set_stop_bits(uint8_t stop_bits);
// 数据位
id(my_uart).set_data_bits(uint8_t data_bits);
// 校验
id(my_uart).set_parity(UARTParityOptions parity);
// 波特率
id(my_uart).set_baud_rate(uint32_t baud_rate);

这种灵活性允许对不同通信需求进行动态适应,增强了 ESPHome 设置的多功能性。

由于主机平台没有物理 UART 引脚,UART 组件使用 Unix 风格端口实现。 你可以指定要使用的端口名称,而不是使用引脚。此实现还支持包含 require_tx 和 require_rx 选项的组件,例如 smt100 等。

# 主机平台的示例配置条目
uart:
baud_rate: 9600
port: "/dev/ttyUSB0"