本帖最后由 ciasdmxhxjjpd@c 于 2022-8-14 16:15 编辑
RT,
之前的帖子,[林内燃气热水器 控制面板接入 ha]
由于uart的一对一通信的特性,主客体之间不能同时出现两个TX端。即不能用2个tx ,接1个rx。但可以用一个tx,接2个rx。
要接入 esphome(中间人),就要把原来的 tx 去掉,让esphome接管 tx,但这会导致 面板按键无法工作,因为按键命令要通过客体的tx发给主体。(主体,主控方。 客体,面板 )。
因此,要实现按键,需要改动的地方比较大。代价比较大。
今天在琢磨,能不能通过esphome 把 客体发过来的数据,无脑转发给 主体。
想了方案。
红线是 主体发过来的数据。esphome 和 客体可以同时接收。esphome接收,要获取状态信息,状态同步,esphome展示要用。客体接收,2位数码管显示等。
蓝色线是,客体发什么,通过中间人,接收后,保存到一个全局的变量中。如图中的虚线,再通过 uart1 的tx 发出去。
黑色线是通过esphome发命令给主体。以实现远程开关热水器,调整温度,设置模式等。
需要在esphome定义2个uart。
uart1 不需要写额外的代码。
uart2 ,需要通过 custom 组件,写个发什么转发什么。
参考官方文档。https://esphome.io/custom/uart.html
uart2 伪代码如下:
1. 定义一个class,继承 UARTDevice 。
2. 复写 loop 函数。在 loop 函数里,先通过available() 判断是否有数据发过来,再把数据暂存在 buffer里,最后通过 write_array() 使用 uart1 发给 主控方?
问题:
因为,客体会定时1s发查询状态的命令。在客体里,程序会根据是否有按键输入,如果有,先发按键命令,再发查询命令,有个先后。
插入esphome后,当通过esphome发命令给主体时(黑线部分),就会出现,发控制命令和查询命令存在同时发送的冲突。可能会存在瑕疵。即如果esphome发的命令和客体发的查询命令时间间隔非常短,就会导致主体来不及处理第二个命令,导致发送的第二个命令失败的情况。
=============================
更新:
黑线发送的时候,加一个条件判断,如果有 esphome 发的命令,先发esphome的命令,再发客体的按键命令。
========================
参考之前德国佬给我写的yeelight c900 的风扇控制外部组件。https://github.com/0neday/yeelig ... ight_fan_controller
改了n遍,终于把它调通了。主要卡在这里,老是读不到数据。
float get_setup_priority() const override
{
// After UART bus
return setup_priority::BUS - 1.0f;
}
要 -1 ,不知道官网为什么不说。
完整代码。gas_water_heater.h
#include "esphome.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
class GasWaterHeaterController : public uart::UARTDevice, public Component
{
protected:
std::vector<uint8_t> rx_buffer_;
uint32_t last_byte_{0};
uint32_t last_send_{0};
public:
GasWaterHeaterController(UARTComponent *parent) : UARTDevice(parent) {}
static const constexpr char *const TAG = "gas_water_heater_controller";
static const uint8_t GAS_PKT_START = 0x02;
static const uint8_t GAS_PKT_END = 0x0D;
static const uint8_t GAS_PKT_COMMAND = 0x055;
static const uint8_t GAS_PKT_QUERY = 0xF6;
uint8_t *send_data = NULL;
uint8_t frame_len = 7;
void send_command()
{
ESP_LOGW(TAG, "TX -> %s", format_hex_pretty(this->send_data, this->frame_len).c_str());
this->write_array(this->send_data, this->frame_len);
this->flush();
}
bool parse_gas_byte_(uint8_t byte)
{
size_t at = this->rx_buffer_.size();
// ESP_LOGI(TAG, "rx_buffer_size: %i ", at);
this->rx_buffer_.push_back(byte);
const uint8_t *raw = &this->rx_buffer_[0];
if (at == 0)
return true;
if (raw[0] != GAS_PKT_START)
{
ESP_LOGW(TAG, "Invalid header");
return false;
}
if (raw[1] == GAS_PKT_QUERY)
this->frame_len = 12;
if (raw[1] == GAS_PKT_COMMAND)
this->frame_len = 7;
// continue recieve byte, until frame_len - 1
if (at < this->frame_len - 1)
return true;
ESP_LOGW(TAG, "RX <- %s", format_hex_pretty(raw, at + 1).c_str());
std::vector<uint8_t> data(this->rx_buffer_.begin(), this->rx_buffer_.begin() + this->frame_len);
this->send_data = &data[0];
// send
this->send_command();
// return false to reset buffer
return false;
}
void dump_config() override
{ // NOLINT(google-readability-function-size,readability-function-size)
ESP_LOGCONFIG(TAG, "GasWaterHeaterController:");
}
float get_setup_priority() const override
{
// After UART bus
return setup_priority::BUS - 1.0f;
}
void setup() override
{
// nothing to do here
}
void loop() override
{
const uint32_t now = millis();
if (now - this->last_byte_ > 50)
{
this->rx_buffer_.clear();
this->last_byte_ = now;
}
while (this->available())
{
uint8_t byte;
this->read_byte(&byte);
if (this->parse_gas_byte_(byte))
{
this->last_byte_ = now;
}
else
{
this->rx_buffer_.clear();
}
}
}
};
esphome
esphome:
name: gas_heater
includes:
- gas_water_heater.h
uart:
- id: to_master
baud_rate: 9600
rx_pin: GPIO16
tx_pin: GPIO17
debug:
direction: BOTH
dummy_receiver: true
- id: from_slave
baud_rate: 9600
rx_pin: GPIO18
tx_pin: GPIO19
debug:
direction: BOTH
dummy_receiver: true
custom_component:
- lambda: |-
auto my_custom = new GasWaterHeaterController(id(from_slave));
return {my_custom};
switch:
# off
- platform: uart
uart_id: to_master
name: gas Toggle On/Off
data: [0x02,0x55,0x20,0x03,0x37,0x38,0x0D]
- platform: uart
uart_id: to_master
name: eco
data: [0x02,0x55,0x29,0x03,0x38,0x31,0x0D]
- platform: uart
uart_id: to_master
name: gas temp +
data: [0x02,0x55,0x30,0x03,0x38,0x38,0x0D]
- platform: uart
uart_id: to_master
name: gas temp -
data: [0x02,0x55,0x31,0x03,0x38,0x39,0x0D]
# 循环发查询命令,保证获取最新的状态。
interval:
- interval: 1s
then:
- uart.write:
id: to_master
data: [0x02,0xF6,0x30,0x34,0x38,0x30,0x30,0x33,0x03,0x32,0x38,0x0D]
效果:
|