『瀚思彼岸』» 智能家居技术论坛

 找回密码
 立即注册
查看: 3331|回复: 9

[技术探讨] 在uart通信主客体之间串联一个uart,以实现类似中间人攻击

[复制链接]

80

主题

388

帖子

1680

积分

金牌会员

Rank: 6Rank: 6

积分
1680
金钱
1292
HASS币
0
发表于 2022-8-12 22:34:10 | 显示全部楼层 |阅读模式
本帖最后由 ciasdmxhxjjpd@c 于 2022-8-14 16:15 编辑

RT,

之前的帖子,[林内燃气热水器 控制面板接入 ha]

由于uart的一对一通信的特性,主客体之间不能同时出现两个TX端。即不能用2个tx ,接1个rx。但可以用一个tx,接2个rx。

要接入 esphome(中间人),就要把原来的 tx 去掉,让esphome接管 tx,但这会导致 面板按键无法工作,因为按键命令要通过客体的tx发给主体。(主体,主控方。 客体,面板 )。
因此,要实现按键,需要改动的地方比较大。代价比较大。

今天在琢磨,能不能通过esphome 把 客体发过来的数据,无脑转发给 主体。

想了方案。


捕获.PNG

红线是 主体发过来的数据。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]
          
         
         
效果:

捕获.PNG


回复

使用道具 举报

5

主题

471

帖子

2114

积分

金牌会员

Rank: 6Rank: 6

积分
2114
金钱
1643
HASS币
0
发表于 2022-8-13 08:20:32 | 显示全部楼层
期待大佬杰作。
回复

使用道具 举报

0

主题

68

帖子

768

积分

高级会员

Rank: 4

积分
768
金钱
700
HASS币
0
发表于 2022-8-13 10:38:50 | 显示全部楼层
TX后面可以加个二极管就行了,不用中继

123.jpg

评分

参与人数 1金钱 +8 收起 理由
sorrypqa + 8 论坛有你更精彩!

查看全部评分

回复

使用道具 举报

80

主题

388

帖子

1680

积分

金牌会员

Rank: 6Rank: 6

积分
1680
金钱
1292
HASS币
0
 楼主| 发表于 2022-8-13 13:49:50 | 显示全部楼层
xlmttkl 发表于 2022-8-13 10:38
TX后面可以加个二极管就行了,不用中继

谢谢,有个问题,要咨询下,比如 从机1 此时要发数据时,拉低 tx,此时主机rx被拉低,从机2.3. 4, 由于二极管单向导通性,tx位的电压高于rx线的电压,不会导通。
但,这时如果 从机1 2 同时发数据呢?是否意味着主机同时接收2个从机发的数据?这里有没有冲突?
回复

使用道具 举报

80

主题

388

帖子

1680

积分

金牌会员

Rank: 6Rank: 6

积分
1680
金钱
1292
HASS币
0
 楼主| 发表于 2022-8-13 13:51:11 | 显示全部楼层
ryanh7 发表于 2022-8-13 09:30
有不同的方法可以处理
可以丢掉面板的查询,esphome来负责查询和控制命令,先后问题可以用队列,两个命令之 ...

就懂个皮毛,不会c++。属于能看懂代码在干嘛的级别,不会写
回复

使用道具 举报

3

主题

69

帖子

825

积分

高级会员

Rank: 4

积分
825
金钱
756
HASS币
10
发表于 2022-8-14 09:48:40 | 显示全部楼层
直接用arduino写esp8266,两个串口透传,用mqtt收发ha的数据
回复

使用道具 举报

80

主题

388

帖子

1680

积分

金牌会员

Rank: 6Rank: 6

积分
1680
金钱
1292
HASS币
0
 楼主| 发表于 2022-8-14 15:12:50 | 显示全部楼层
hyq 发表于 2022-8-13 08:20
期待大佬杰作。

可以去干了啊!
回复

使用道具 举报

80

主题

388

帖子

1680

积分

金牌会员

Rank: 6Rank: 6

积分
1680
金钱
1292
HASS币
0
 楼主| 发表于 2022-8-14 15:16:36 | 显示全部楼层
ryanh7 发表于 2022-8-13 09:30
有不同的方法可以处理
可以丢掉面板的查询,esphome来负责查询和控制命令,先后问题可以用队列,两个命令之 ...

代码已更新,麻烦有空看下有么有优化的地方。
回复

使用道具 举报

80

主题

388

帖子

1680

积分

金牌会员

Rank: 6Rank: 6

积分
1680
金钱
1292
HASS币
0
 楼主| 发表于 2022-8-14 16:07:22 | 显示全部楼层
ryanh7 发表于 2022-8-14 15:30
parse_gas_byte_的at == 0下面的几行判断效率和逻辑都有优化的余地,不过改不改执行压力不大。其实还可以 ...

对, 类似于德国佬写的 风扇速度控制,把 uart write 写到 fan 的 control 里一样。

回复

使用道具 举报

5

主题

471

帖子

2114

积分

金牌会员

Rank: 6Rank: 6

积分
2114
金钱
1643
HASS币
0
发表于 2022-9-15 09:56:59 | 显示全部楼层
ciasdmxhxjjpd@c 发表于 2022-8-14 16:07
对, 类似于德国佬写的 风扇速度控制,把 uart write 写到 fan 的 control 里一样。

...

大佬读取温度值搞出来了没有?我不会
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Hassbian

GMT+8, 2024-11-29 04:02 , Processed in 0.184749 second(s), 36 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表