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

 找回密码
 立即注册
查看: 5023|回复: 17

[硬件DIY] ESP32+RF433接收发送组RF桥

[复制链接]

20

主题

216

帖子

1458

积分

论坛DIY达人

积分
1458
金钱
1217
HASS币
100
发表于 2023-7-1 22:42:34 | 显示全部楼层 |阅读模式
本帖最后由 zro22 于 2024-5-24 21:59 编辑

硬件架构很简单ESP32外接蜂鸟远-R1和远-T2L模块作为收发射频
实现两个功能
1,可以通过指定MQTT填写内容后,转换成射频码发送出去,实现比如HA控制灯开关(无状态反馈)。
2,射频遥控器发码后 ESP32接收到 转发到HA或者MQTT,HA里面通过自动化控制设备。
可以用现成的RF-bridge
https://bbs.hassbian.com/thread-14172-1-1.html
本着DIY的原则,我自己画了个小板子,板子上有433和315两套,不过我只用433所以只买了433模块

PIC20230701223422001.png
PIC20230701222900001.png

ESPhome的代码

esphome:
  name: rfbridge
  friendly_name: RFBridge

esp32:
  board: nodemcu-32s
  framework:
    type: arduino
#external_components:
#  - source: my_components
#    components: [ remote_base, remote_receiver, remote_transmitter] 
# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "oio02kQmD6MJn6zWnZx5lAbSWyYIYddpwYGipMy/1hY="
  services:
    - service: rf_433_send_raw
      variables:
        code: string
        repeat: int
      then:
        - remote_transmitter.transmit_rc_switch_raw:
            code: !lambda 'return code;'
            repeat:
              times: !lambda 'return repeat;'
              wait_time: 0.01s
    - service: rf_433_send_typea
      variables:
        repeat: int
        group: string
        device: string
        state: int
      then:
        - remote_transmitter.transmit_rc_switch_type_a:
            repeat:
              times: !lambda 'return repeat;'              
            group: !lambda 'return group;'
            device: !lambda 'return device;'
            state: !lambda 'return state;'
            protocol: 1
ota:
  password: "f6b09c4d30124fc97f22dd74899c5342"

wifi:
  ssid: 2.4ghome
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Rfbridge Fallback Hotspot"
    password: "iTbOmVzigCuo"
  use_address: 192.168.10.40 

captive_portal:
    
web_server:
  port: 80
globals:
  - id: global_group
    type: int
    restore_value: no
    initial_value: '0'
  - id: global_device
    type: int
    restore_value: no
    initial_value: '0'  
  - id: global_state
    type: int
    restore_value: no
    initial_value: '0'      
  - id: global_rcvstr
    type: std::string
    restore_value: no  # Strings cannot be saved/restored    
  - id: global_rcvvalstr
    type: std::string
    restore_value: no  # Strings cannot be saved/restored        
  - id: global_rf_rcvd
    type: bool
    restore_value: no
    initial_value: '0'
  - id: global_oldrcv
    type: long long int
    restore_value: no
    initial_value: '0'
  - id: global_oldrcv2
    type: long long int
    restore_value: no
    initial_value: '0'    
  - id: global_oldrcvtime
    type: int
    restore_value: no
    initial_value: '0'
  - id: global_oldrcvtime2
    type: int
    restore_value: no
    initial_value: '0'    
  - id: global_rcv_skip
    type: int
    restore_value: no
    initial_value: '0'
  - id: global_same_cnt
    type: int
    restore_value: no
    initial_value: '0'
  - id: global_snd_grop
    type: std::string
    restore_value: no
  - id: global_snd_dev
    type: std::string
    restore_value: no
  - id: global_snd_state
    type: int
    restore_value: no 
    initial_value: '0'      
mqtt:
  broker: 192.168.xx.xxx
  username: xxx
  password: xxxt
  on_json_message:
    - topic: rfbridge/rftosend/raw
      qos: 0
      then:
        - remote_transmitter.transmit_rc_switch_raw:
            code: !lambda |-
                  int code = 0;
                  int i=0;
                  char strcode[25];
                  strcode[24]=0;
                  if (x.containsKey("code")){
                  code =  x["code"];
                  ESP_LOGD("RFRAWSND", "%d",code);
                  for(i=0;i<24;i++)
                  {
                    strcode[24-i-1] = '0';
                    if(code&1)
                      strcode[24-i-1] = '1';
                    code=code/2;
                    //ESP_LOGD("MQSEND", "%c",strcode[i]);
                  }
                  //itoa(code,strcode,2);
                  ESP_LOGD("RFRAWSND", "%s",strcode);
                  return strcode;}
                  return 0;
            protocol: 1
    - topic: rfbridge/rftosend/atype
      qos: 0
      then:
        - remote_transmitter.transmit_rc_switch_type_a:
            group: !lambda |-
                  /*{
                    "group": "25",  max31
                    "device": "18", max31
                    "state": 1  1 or 0
                  }*/
                  int grop = 0;
                  int i=0;
                  char strcode[6];
                  strcode[5]=0;
                  id(global_snd_grop) = "0";
                  if (x.containsKey("group")){
                  grop =  x["group"];
                  id(global_snd_grop) = grop; 
                  //ESP_LOGD("RFASND", "%d",grop);
                  /*strcode[0] = grop/10000 + 0x30;
                  strcode[1] = (grop%10000)/1000 + 0x30;
                  strcode[2] = (grop%1000)/100 + 0x30;
                  strcode[3] = (grop%100)/10 + 0x30;
                  strcode[4] = (grop%10) + 0x30 ;*/
                  for(i=0;i<5;i++)
                  {
                    strcode[4-i] = (grop&1) + 0x30;
                    grop = grop/2;
                  }
                  ESP_LOGD("RFASND", "Group:%s",strcode);
                  id(global_snd_grop) = strcode;
                  return strcode;}
                  return 0;
            device: !lambda |-
                  int devc = 0;
                  int i=0;
                  char strcode[6];
                  strcode[5]=0;
                  id(global_snd_dev) = "0";
                  if (x.containsKey("device")){
                  devc =  x["device"];
                  id(global_snd_dev) = devc;
                  //ESP_LOGD("RFASND", "%d",devc);
                  /*strcode[0] = devc/10000 + 0x30;
                  strcode[1] = (devc%10000)/1000 + 0x30;
                  strcode[2] = (devc%1000)/100 + 0x30;
                  strcode[3] = (devc%100)/10 + 0x30;
                  strcode[4] = (devc%10) + 0x30;*/
                  for(i=0;i<5;i++)
                  {
                    strcode[4-i] = (devc&1) + 0x30;
                    devc = devc/2;
                  }
                  ESP_LOGD("RFASND", "Device:%s",strcode);
                  id(global_snd_dev) = strcode;
                  return strcode;}
                  return 0;
            state: !lambda |-
                   int stat;
                   stat = x["state"];
                   id(global_snd_state) = stat;
                   ESP_LOGD("RFASND", "state:%d",stat);
                   return stat;
            protocol: 1  
            repeat:
              times: 7
              wait_time : 30ms 
                     
remote_receiver:
  pin: 
    number: GPIO19
    #inverted: True
    mode: INPUT_PULLUP
  dump: 
    - rc_switch
  # Settings to optimize recognition of RF devices
  tolerance: 20%
  filter: 250us
  idle: 6ms
#  buffer_size: 3kb
  on_rc_switch:
    then: 
      - homeassistant.event:
          event: esphome.rf_code_received
          data:
            code: !lambda |- 
                //'char buffer [10];return itoa(data.code,buffer,16);'
                char rcvdata[80];
                std::string nullbuf;
                int grop=0;
                int devc=0;
                int channel=0;
                int cmd=0;
                int subcmd=0;
                int i;
                int curtime;
                long long temp;
                int state=0;
                id(global_rcv_skip) = 0;
                //root["code"] = x.code;
                temp = x.code;
                ESP_LOGD("RCV", "temp:%llx",temp);
                sprintf(rcvdata, "%llx", temp);
                id(global_rcvvalstr) = rcvdata;
                //if(temp>0xffffffff)
                curtime = millis();
                if(id(global_oldrcv)==temp)
                {
                  if(curtime-id(global_oldrcvtime)<120)
                  {
                    id(global_rcv_skip) = 1;
                    id(global_same_cnt)++;
                  }else
                    id(global_same_cnt) = 0;
                }
                else if(id(global_oldrcv2)==temp)
                {
                  if(curtime-id(global_oldrcvtime2)<120)
                  {
                    id(global_rcv_skip) = 1;
                    id(global_same_cnt)++;
                  }else
                    id(global_same_cnt) = 0;
                }else
                  id(global_same_cnt)=0;
                //if(((temp>>1)&0x3)==0)
                //  id(global_rcv_skip) = 1;
                //if((id(global_same_cnt)>=20)&&(id(global_same_cnt)%2==0)) 
                //{
                //  id(global_rcv_skip) = 0; 
                //}
                if(id(global_same_cnt)==0)
                {
                  id(global_rcv_skip) = 0;
                }
                ESP_LOGD("RCV", "Millis() %d diff: %d skip:%d cnt:%d", curtime,curtime-id(global_oldrcvtime),id(global_rcv_skip),id(global_same_cnt));
                id(global_oldrcv2) = id(global_oldrcv);
                id(global_oldrcv) = temp;
                //if(id(global_rcv_skip)==0)
                id(global_oldrcvtime2) = id(global_oldrcvtime);
                id(global_oldrcvtime) = curtime;
                if(((temp>>16)&0xffffff)==0x3191f9)
                {
                  sprintf(rcvdata, "%llx", temp>>3);
                  //root["dooya"] = rcvdata;
                  grop = (temp>>19);
                  devc = (temp&0x7ffff) >> 11;
                  channel = (temp&0x7ff) >> 7;
                  cmd = (temp&0x7f) >> 3;
                  subcmd = (temp&0x7);
                  //ESP_LOGD("RFDOOYA", "Group:%x",grop);
                  //ESP_LOGD("RFDOOYA", "Device:%x",devc);
                  //ESP_LOGD("RFDOOYA", "Channel:%x",channel);
                  //ESP_LOGD("RFDOOYA", "Command:%x",cmd);
                  //ESP_LOGD("RFDOOYA", "SubCommand:%x",subcmd);
                  id(global_rcvstr) = rcvdata;
                  return id(global_rcvstr);
                }
                if(temp<0x555555)
                {
                  for(i=0;i<24;i++)
                  {
                    rcvdata[24-i-1]=0;
                    if(temp&1)
                      rcvdata[24-i-1]=1;
                    temp = temp/2;
                  }
                  //for(i=0;i<24;i++)
                  //  ESP_LOGD("RCV", "%d,%d",i,rcvdata[i]);
                  for(i=0;i<5;i++)
                  {
                    if((rcvdata[1+2*i]==0)&&(rcvdata[2+2*i]==0))
                      grop += 1;
                    if(i==4)
                      break;
                    grop *=2; 
                  }  
                  //ESP_LOGD("RCV", "Group:%d",grop);
                  for(i=0;i<5;i++)
                  {
                    if((rcvdata[11+2*i]==0)&&(rcvdata[12+2*i]==0))
                      devc += 1;
                    if(i==4)
                      break; 
                    devc *=2; 
                  }  
                  //ESP_LOGD("RCV", "Device:%d",devc);
                  if((rcvdata[21]==0)&&(rcvdata[22]==0)&&(rcvdata[23]==0))
                      state = 1;
                  else if((rcvdata[21]==0)&&(rcvdata[22]==1)&&(rcvdata[23]==0))
                      state = 0; 
                  else 
                  {
                    grop = 0;
                    devc = 0;
                    state = 0; 
                    id(global_rcv_skip) = 1;
                  } 
                  ESP_LOGD("RCV", "skip %d", id(global_rcv_skip));     
                  //ESP_LOGD("RCV", "State:%d",state);
                  id(global_group) = grop;
                  id(global_device) = devc;
                  id(global_state) = state;
                  sprintf(rcvdata, "%02d", grop);
                  //root["Group"] = rcvdata;//grop;
                  sprintf(rcvdata, "%02d", devc);
                  //root["Device"] = rcvdata;//devc;
                  sprintf(rcvdata, "%d", state);
                  //root["State"] = rcvdata;//state;
                  sprintf(rcvdata, "%02d%02d%d", grop,devc,state);
                  //root["msg"] = rcvdata;
                  id(global_rcvstr) = rcvdata;
                }
                return id(global_rcvstr);
            value: !lambda 'return id(global_rcvvalstr);'
            codenum: !lambda return id(global_group)*1000+id(global_device)*10+id(global_state);
            onecode: !lambda |- 
                    if(id(global_rcv_skip)==1)
                    {
                      return 10000;
                    }else
                      return id(global_group)*100+id(global_device);
      #- mqtt.publish_json:
      #    topic: rfbridge/rfrcv
      #    payload: !lambda |-
            
      - mqtt.publish:
          topic: !lambda |- 
               if(id(global_rcv_skip)==1)
               {                 
                 return "rfbridge/rcvstrnull";
               }
               else
                 return "rfbridge/rcvstr";
          payload: !lambda |- 
               if(id(global_rcv_skip)==1)
               {                 
                 return "0";
               }
               else
                 return id(global_rcvstr);      
remote_transmitter:
  - pin: GPIO21
#    id: tr433
    # RF uses a 100% carrier signal
    carrier_duty_percent: 100%
#  - pin: GPIO20
#    id: tr315
#    carrier_duty_percent: 100%
switch:
  - platform: template
    name: swforrfsnd
    id: rfsw
    turn_on_action:
      - delay: 0.2s
      - switch.turn_off: rfsw
#下面的代码几个开关测试RF发送,没什么用
  - platform: template
    name: RF2 raw Button
    turn_on_action:
      - remote_transmitter.transmit_rc_switch_raw:
          code: '100010000000000010111110'
          protocol: 2  
  - platform: template
    name: RFA1 Power Button
    turn_on_action:
      - remote_transmitter.transmit_rc_switch_type_a:
          group: '01001'
          device: '10110'
          state: off
          protocol: 1
    turn_off_action:
      - remote_transmitter.transmit_rc_switch_type_a:
          group: '01001'
          device: '10110'
          state: on
          protocol: 1        
  - platform: template
    name: RFA2 Power Button
    turn_on_action:
      - remote_transmitter.transmit_rc_switch_type_a:
          group: '01001'
          device: '10111'
          state: off
          protocol: 1
    turn_off_action:
      - remote_transmitter.transmit_rc_switch_type_a:
          group: '01001'
          device: '10111'
          state: on
          protocol: 1  
  - platform: template
    name: RFCA Power Button
    turn_on_action:
      - remote_transmitter.transmit_rc_switch_type_c:
          family: 'c'
          group: 3
          device: 1
          state: off
          protocol: 1
    turn_off_action:
      - remote_transmitter.transmit_rc_switch_type_c:
          family: 'c'
          group: 3
          device: 1
          state: on
          protocol: 1          
  - platform: template
    name: RFCC Power Button
    turn_on_action:
      - remote_transmitter.transmit_rc_switch_type_c:
          family: 'a'
          group: 3
          device: 1
          state: off
          protocol: 1
    turn_off_action:
      - remote_transmitter.transmit_rc_switch_type_c:
          family: 'a'
          group: 3
          device: 1
          state: on
          protocol: 1                    

发送的时候在这个topicrfbridge/rftosend/atype
publish如下格式,我这里写了循环发送7次,间隔30ms
{
                    "group": "25",
                    "device": "18",
                    "state": 1
                  }
接收的时候收到的码会publish到这个topic
rfbridge/rcvstr 数据我做了一个处理,具体可以见程序

这里面switch都是没有用的RF接收后有一些处理,主要是射频按一次按键会发送好多次,如果只发送一次可能接收端收不到,所以要发很多次,这就造成一个问题,如果是用来控制开关,会出现反复开关,所以我尝试加了一些过滤,多短时间内收到同样的码给过滤掉。但是这样还是会出现其他问题,比如用来控制上下的时候,连击会比较慢。所以我最后射频只用来开或者关。
如下这个问题,我后来发现改成杜亚的这个格式之后识别成功率不好,所以我放弃了,还是用esphome原本的type1.
还有另外一个问题,你手上的遥控器发送的波形可能ESPHOME无法识别。
具体看这个贴子
https://bbs.hassbian.com/thread-14331-1-1.html
比如杜亚的就无法识别,我是用逻辑分析仪抓好波形之后,按照上面贴子的方法修改了源文件
修改源文件需要用DEV版本的eSPHOME
具体需要修改的源文件在
/volume2/docker/esphomedev/.esphome/build/rfbridge/src/esphome/components/remote_base/rc_switch_protocol.cpp
可以把其他都删了,只保留你手上遥控器的格式,这样识别正确率高一些 PIC20230701225001001.png

如下是blueprint因为我设定的是开和关只有最后一位不一样,比如
AABB0是关 AABB1是开,所以
把AABB和对应的设备关联就可以了,value_a填AABB,light_a选择对应的灯
PIC20230703103341001.png
blueprint:
  name: rf bridge receiver to trigger switch or light 4 cnt
  description: create 4 rf bridge or other mqtt topic value to control light or switch
  domain: automation
  input:
    value_a:
      selector:
        number:
    value_b:
      selector:
        number:
    value_c:
      selector:
        number:
    value_d:
      selector:
        number:        
    light_a:
      selector:
        entity:
          domain:
          - light
          - switch
          multiple: true    
    light_b:
      selector:
        entity:
          domain:
          - light
          - switch
          multiple: true    
    light_c:
      selector:
        entity:
          domain:
          - light
          - switch
          multiple: true    
    light_d:
      selector:
        entity:
          domain:
          - light
          - switch
          multiple: true    
          
trigger:
#  platform: mqtt
#  topic: !input mqtttopic
  platform: event
  event_type: esphome.rf_code_received
#  event_data:
#    code: !input rf_code
action:
- variables:
#    payload: "{{ trigger.payload }}"
    payload: "{{ trigger.event.data.code }}"
    value_a: !input value_a 
    value_b: !input value_b 
    value_c: !input value_c 
    value_d: !input value_d 
- choose:
  - conditions:
    - "{{ payload == value_a*10 }}"
    sequence: 
      - service: switch.turn_off
        data:
          entity_id: !input light_a
      - service: light.turn_off
        data:
          entity_id: !input light_a    
  - conditions:
    - "{{ payload == value_a*10+1 }}"
    sequence: 
      - service: switch.turn_on
        data:
          entity_id: !input light_a    
      - service: light.turn_on
        data:
          entity_id: !input light_a        
  - conditions:
    - "{{ payload == value_b*10 }}"
    sequence: 
      - service: switch.turn_off
        data:
          entity_id: !input light_b
      - service: light.turn_off
        data:
          entity_id: !input light_b    
  - conditions:
    - "{{ payload == value_b*10+1 }}"
    sequence: 
      - service: switch.turn_on
        data:
          entity_id: !input light_b    
      - service: light.turn_on
        data:
          entity_id: !input light_b        
  - conditions:
    - "{{ payload == value_c*10 }}"
    sequence: 
      - service: switch.turn_off
        data:
          entity_id: !input light_c
      - service: light.turn_off
        data:
          entity_id: !input light_c    
  - conditions:
    - "{{ payload == value_c*10+1 }}"
    sequence: 
      - service: switch.turn_on
        data:
          entity_id: !input light_c
      - service: light.turn_on
        data:
          entity_id: !input light_c        
  - conditions:
    - "{{ payload == value_d*10 }}"
    sequence: 
      - service: switch.turn_off
        data:
          entity_id: !input light_d
      - service: light.turn_off
        data:
          entity_id: !input light_d    
  - conditions:
    - "{{ payload == value_d*10+1 }}"
    sequence: 
      - service: switch.turn_on
        data:
          entity_id: !input light_d    
      - service: light.turn_on
        data:
          entity_id: !input light_d      







评分

参与人数 2金钱 +30 HASS币 +20 收起 理由
admin + 20 + 20 高手,这是高手!
sorrypqa + 10 大神666!

查看全部评分

回复

使用道具 举报

24

主题

581

帖子

2693

积分

金牌会员

Rank: 6Rank: 6

积分
2693
金钱
2112
HASS币
0
发表于 2023-7-1 23:02:34 | 显示全部楼层
esphome不是有api组件吗?
你一个433就用esp32
我海鲜市场搞得那个欧普的遥控器
红外射频共6个组件都只用8266
结果就是搞了这6组件加api发射后再加开关和按键组件就会死机
回复

使用道具 举报

20

主题

216

帖子

1458

积分

论坛DIY达人

积分
1458
金钱
1217
HASS币
100
 楼主| 发表于 2023-7-1 23:06:53 | 显示全部楼层
kjjuhfv 发表于 2023-7-1 23:02
esphome不是有api组件吗?
你一个433就用esp32
我海鲜市场搞得那个欧普的遥控器

所以我基本都用ESP32,不用8266,没差几块钱
回复

使用道具 举报

20

主题

216

帖子

1458

积分

论坛DIY达人

积分
1458
金钱
1217
HASS币
100
 楼主| 发表于 2023-7-1 23:07:17 | 显示全部楼层
kjjuhfv 发表于 2023-7-1 23:02
esphome不是有api组件吗?
你一个433就用esp32
我海鲜市场搞得那个欧普的遥控器

API发射怎么调用?
回复

使用道具 举报

24

主题

581

帖子

2693

积分

金牌会员

Rank: 6Rank: 6

积分
2693
金钱
2112
HASS币
0
发表于 2023-7-1 23:11:41 | 显示全部楼层
zro22 发表于 2023-7-1 23:07
API发射怎么调用?

api:
  services:
    - service: rf_433_send
      variables:
        code: string
        repeat: int
      then:
        - remote_transmitter.transmit_rc_switch_raw:
            code: !lambda 'return code;'
            repeat:
              times: !lambda 'return repeat;'

我也是抄大佬的
回复

使用道具 举报

24

主题

581

帖子

2693

积分

金牌会员

Rank: 6Rank: 6

积分
2693
金钱
2112
HASS币
0
发表于 2023-7-1 23:13:30 | 显示全部楼层
zro22 发表于 2023-7-1 23:06
所以我基本都用ESP32,不用8266,没差几块钱

厂家这样设计也没办法
挺想把8266拆下来的 可惜没有设备
回复

使用道具 举报

51

主题

1341

帖子

5604

积分

论坛DIY达人

积分
5604
金钱
4263
HASS币
20
发表于 2023-7-2 09:07:54 | 显示全部楼层
zro22 发表于 2023-7-1 23:06
所以我基本都用ESP32,不用8266,没差几块钱

对的!ESP32比8266好用太多,而且也不贵,10元就能买到
回复

使用道具 举报

20

主题

216

帖子

1458

积分

论坛DIY达人

积分
1458
金钱
1217
HASS币
100
 楼主| 发表于 2023-7-2 11:46:33 | 显示全部楼层
kjjuhfv 发表于 2023-7-1 23:11
api:
  services:
    - service: rf_433_send

知道 protocol:  这个参数怎么传吗?
他的定义是rcbase 变量怎么传过去呢?
回复

使用道具 举报

24

主题

581

帖子

2693

积分

金牌会员

Rank: 6Rank: 6

积分
2693
金钱
2112
HASS币
0
发表于 2023-7-2 13:48:03 | 显示全部楼层
zro22 发表于 2023-7-2 11:46
知道 protocol:  这个参数怎么传吗?
他的定义是rcbase 变量怎么传过去呢?

都说我抄大佬作业的啦
以前我看到过这个参数的代码
然而我没有保存下来
回复

使用道具 举报

8

主题

158

帖子

1227

积分

金牌会员

Rank: 6Rank: 6

积分
1227
金钱
1069
HASS币
0
发表于 2023-7-2 16:34:25 | 显示全部楼层
楼主您好,请问可以用这个接收并存储433信号,然后在ha建一个播放按钮吗?比如学习开门的信号,然后在ha添加一个开门的按钮,可以直接在ha里开关门。
回复

使用道具 举报

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

本版积分规则

Archiver|手机版|小黑屋|Hassbian

GMT+8, 2024-11-26 06:42 , Processed in 0.082566 second(s), 35 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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