自制RF桥接入MQTT和HA
RF桥实现两个功能:
一个是接受到射频遥控器指令后给对应的MQTT publish接收到的内容,这样可以实现用射频遥控器控制HA的设备。顺便说一下,我用的是超遥万能遥控器。
一个是在对应的MQTT topic publish内容后通过射频遥控器发送出去,这样可以实现通过HA控制射频设备。
硬件架构
硬件架构很简单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模块


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: xxxxx
password: xxxxx
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:
#下面的代码几个开关测试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
代码说明
代码里的几个switch都没有用,是测试发送RF码用的。
需要RF桥发送射频的时候,在这个topic ’rfbridge/rftosend/atype‘ publish如下格式,esphome监测到后,会循环发送7次,间隔30ms
{
"group": "25",
"device": "18",
"state": 1
}
射频接收的时候,收到的码会publish到这个topic
rfbridge/rcvstr
数据我做了一个处理,具体可以见程序
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
可以把其他都删了,只保留你手上遥控器的格式,这样识别正确率高一些
如下是HA里面的blueprint,实现RF遥控器控制对应的灯。
因为我设定的是开和关只有最后一位不一样,比如
AABB0是关 AABB1是开,所以把AABB和对应的设备关联就可以了,value_a填AABB,light_a选择对应的灯

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
#HA(1)文章作者:Administrator
文章链接:https://www.nasnas.site//archives/zi-zhi-rfqiao-jie-ru-mqtthe-ha
版权声明:本博客所有文章除特别声明外,均采用CC BY-NC-SA 4.0 许可协议,转载请注明出处!
评论
