any examples pls?
only as mentioned above - the Bond Bridge. I think there have been attempts to use RFX-TRX, not sure if successfully.
Did you have to send the hub back to them in order to do the RF Firmware update? I had already sent back my unit to them last year as part of their proactive recall of the hub memory issues, but they obviously didnât touch the RF Firmware on my unit because its still at B10.
For What itâs worth I have 2 Hubs and 2 Bond Bridges.
I would love to rely on Hubs exclusively but they go offline from time to time. Sometimes itâs Automate home assistant integration, and sometimes the hubs.
I find bond bridges incredibly reliable. So all my Automations use Bond Bridge. But I use Hubs for Data on batteries, accurate blind positioning, etc.
In addition I power the hubs via zigbee smart switches, If a number of shade sensors go offline an automation runs to restart the Pulse 2 and then reload the integration
I also got sent new B17 hubs and they didnt ask for the old B10 hub back.
So the other day the two B17 RF edition hubs were going offline repeatedly. so i sniffed the traffic. Turns out my hubs are talking to AWS IOTs service in US East Coast and im in New Zealand so pinging the captured DNS endpoint name results in about 200ms delay. I sent a support email to Rollease and have had radio silence.
So i cracked open the shell on the spare B10 i have been left with.
TLDR;
I have a working ESPHome Config for it.
Chip is ESP32-D0WD-V3 (revision 3)
Working LAN8720A from SMSC
Working I2C (2 devices, * PCA9536: 0x41
, ATECC508A @ 0/0x58 secure element)
Working tri colour leds driving by IO 0,1,2 on the PCA9536
Working uart to the STM32L051 micro that is connected to the RF chip
Buttons mapped to GPIO36 (external) GPIO39 Internal button
Winbond 4MB flash (just works)
Quering the STM via the uart interface:
09:11:19 [I] [RF_A:201]
TX: !000v?;
09:11:19 [D] [RF_A:287]
!BR1vB10;
So im getting back the Bridge Version B10 from the STM32.
A i had to re pair all my blinds so the stored IDs do not match the current pairing IDâs on the B17 hubs.
09:14:26 [I] [RF_A:201]
TX: !USZo;
09:14:27 [D] [RF_A:287]
!USZEnp;
So the STM32 returns the ID with Enp, Error not present.
From reading its all down now to the pairing of the shades.
Ill be testing with my office blind to see if i can get it paired whit the B10 hub.
Other observations on the board:
The last test pad by the Ethernet port is the enable programing mode when you reset with en pin which is exposed and labeled on a test pad also.
The tx rx pads a also clearly labeled.
The factory firmware is mongoose OS (AWS IOT Framework)
Tracing out the other tx rx pads they connect to the STM32 so dumping the flash from this and replacing it with the B17 firmware would also be viable. Would have to extract it from one of the B17 hubs. this requires a different tool to the esptool used to dump the original firmware
#dump the original rom when in download mode
esptool.exe --port COM6 read_flash 0x00000 0x400000 dump.bin
#write the dump when in download mode ie. restore the original firmware
esptool.exe --chip esp32 --port COM6 write_flash 0x0000 dump.bin
To flash the esphome image use the manual download legacy OTA format and use ESPHome-Flasher (1.4.0) to upload the image, once you have the image on you can use wirelessly if you make changes. the version of the tool doesnât matter i would susspect.
Using the Pulse LINQ is a good way to discover all the commands the STM32 will accept.
esphome:
name: pulse2hubdev
friendly_name: pulse2hubdev
on_boot:
priority: -100
then:
- switch.turn_on: green
esp32:
board: esp32dev
framework:
type: arduino
# Logging
logger:
level: DEBUG
logs:
ethernet: INFO
i2c: INFO
text_sensor: INFO
sensor: INFO
uart: DEBUG
baud_rate: 0 # disables default serial logging so UART pins are free
ethernet:
type: LAN8720
mdc_pin: GPIO16
mdio_pin: GPIO23
clk:
pin: GPIO0
mode: CLK_EXT_IN
phy_addr: 1
power_pin: GPIO2
# wifi:
# networks:
# - ssid: !secret wifi_ssid2
# password: !secret wifi_password2
api:
encryption:
key: "W2NcdU3qMfp7UQbgS832RIP+DWCqQbjgZeVztbov2CY="
ota:
- platform: esphome
password: "cb2a0f65d905cf5e8509ac73ab55a7ee"
web_server:
port: 80
i2c:
id: i2c_bus
sda: GPIO14
scl: GPIO4
frequency: 100kHz
scan: false
pca9554:
- id: 'pca9554a_device'
i2c_id: i2c_bus
address: 0x41
switch:
- platform: gpio
name: "Red Led"
id: red
pin:
pca9554: pca9554a_device
number: 0
mode:
output: true
inverted: true
- platform: gpio
name: "Blue Led"
id: blue
pin:
pca9554: pca9554a_device
number: 1
mode:
output: true
inverted: true
- platform: gpio
name: "Green Led"
id: green
pin:
pca9554: pca9554a_device
number: 2
mode:
output: true
inverted: true
- platform: gpio
name: "Switch4"
pin:
pca9554: pca9554a_device
number: 4
mode:
output: true
inverted: false
sensor:
- platform: uptime
name: "Uptime"
text_sensor:
- platform: version
name: "ESPHome Version"
- platform: ethernet_info
ip_address:
name: "ETH IP"
mac_address:
name: "ETH MAC"
dns_address:
name: "ETH DNS"
binary_sensor:
- platform: gpio
name: "GPIO36"
pin:
number: GPIO36
mode: { input: true }
inverted: true
- platform: gpio
name: "Status Line (GPIO39)"
pin:
number: GPIO39
mode:
input: true
inverted: true # flip to true if the logic is active-low
uart:
- id: rf_a
rx_pin: GPIO13 # STM32 TX -> ESP RX
tx_pin: GPIO12 # STM32 RX <- ESP TX (strap pin is safer as TX)
baud_rate: 115200
data_bits: 8
parity: NONE
stop_bits: 1
rx_buffer_size: 4096
globals:
- id: ascii_rf
type: std::string
- id: raw_rf
type: std::vector<uint8_t>
- id: last_rx_ms
type: uint32_t
initial_value: '0'
script:
- id: rf_send_ascii_semicolon
mode: restart
parameters:
s: std::string
then:
- lambda: |-
std::string msg = s;
if (msg.empty() || msg[0] != '!') msg.insert(0, "!");
if (msg.back() != ';') msg.push_back(';');
id(rf_a)->write_str(msg.c_str()); // <-- convert to const char*
ESP_LOGI("RF_A","TX: %s", msg.c_str());
text:
- platform: template
id: blind_id
name: "Dooya Blind ID"
mode: TEXT
initial_value: "KV8"
optimistic: true
min_length: 1
max_length: 8
button:
- platform: template
name: "Bridge: GLOBALVERSION (!000v?;)"
on_press:
- script.execute: { id: rf_send_ascii_semicolon, s: "!000v?;" }
- platform: template
name: "Bridge: GLOBALPOSITION (!000r?;)"
on_press:
- script.execute: { id: rf_send_ascii_semicolon, s: "!000r?;" }
- platform: template
name: "Bridge: DAT (flush/request)"
on_press:
- script.execute: { id: rf_send_ascii_semicolon, s: "!DAT;" }
- platform: template
name: "Blind Open (!ID o;)"
on_press:
- lambda: |-
std::string b = id(blind_id).state.c_str();
id(rf_send_ascii_semicolon).execute("!" + b + "o;");
- platform: template
name: "Blind Close (!ID c;)"
on_press:
- lambda: |-
std::string b = id(blind_id).state.c_str();
id(rf_send_ascii_semicolon).execute("!" + b + "c;");
- platform: template
name: "Blind Stop (!ID s;)"
on_press:
- lambda: |-
std::string b = id(blind_id).state.c_str();
id(rf_send_ascii_semicolon).execute("!" + b + "s;");
- platform: template
name: "Blind Possition (!ID pP?;)"
on_press:
- lambda: |-
std::string b = id(blind_id).state.c_str();
id(rf_send_ascii_semicolon).execute("!" + b + "pP?;");
- platform: template
name: "Blind Pair (!000&;)"
on_press:
- lambda: |-
id(rf_send_ascii_semicolon).execute("!000&;");
interval:
- interval: 20ms
then:
- lambda: |-
auto &u = *id(rf_a);
bool got = false;
uint8_t c;
while (u.available() && u.read_byte(&c)) {
got = true;
id(last_rx_ms) = millis();
// Build pretty ASCII (printable -> char, else '.')
char shown = (c >= 32 && c <= 126) ? (char)c : '.';
if (shown != '\r' && shown != '\n') id(ascii_rf).push_back(shown);
id(raw_rf).push_back(c);
// Flush a frame on ';' (Dooya frames) OR on newline
if (c == ';' || c == '\n') {
// Log ASCII frame
if (!id(ascii_rf).empty()) {
ESP_LOGD("RF_A", "%s", id(ascii_rf).c_str());
} else {
ESP_LOGD("RF_A", "<blank frame>");
}
// Also show hex of the same frame
char line[3*256 + 1]; size_t n = id(raw_rf).size(); if (n > 256) n = 256;
char *p = line;
for (size_t i = 0; i < n; i++) { sprintf(p, "%02X ", id(raw_rf)[i]); p += 3; }
*p = 0;
ESP_LOGV("RF_A", "[HEX] %s", line);
id(raw_rf).clear();
id(ascii_rf).clear();
}
}
// Safety: if bytes stopped mid-frame, dump what we have after 40 ms
if (!got && !id(raw_rf).empty() && (millis() - id(last_rx_ms) > 40)) {
char line[3*256 + 1]; size_t n = id(raw_rf).size(); if (n > 256) n = 256;
char *p = line;
for (size_t i = 0; i < n; i++) { sprintf(p, "%02X ", id(raw_rf)[i]); p += 3; }
*p = 0;
ESP_LOGD("RF_A", "[RAW timeout] %s", line);
id(raw_rf).clear();
id(ascii_rf).clear();
}
Update have it working and pairing an existing blind to the bridge running ESPHome
[14:48:16.621][D][button:023]: 'Bridge: GLOBALVERSION (!000v?;)' Pressed.
[14:48:16.626][I][RF_A:221]: TX: !000v?;
[14:48:16.659][D][RF_A:303]: !BR1vB10;
[14:48:16.663][V][RF_A:313]: [HEX] 21 42 52 31 76 42 31 30 3B
[14:48:26.669][D][button:023]: 'Blind Pair (!000&;)' Pressed.
[14:48:26.673][I][RF_A:221]: TX: !000&;
[14:48:27.904][D][RF_A:303]: !JJ8Enl;
[14:48:27.907][V][RF_A:313]: [HEX] 21 4A 4A 38 45 6E 6C 3B
[14:58:06.744][D][button:023]: 'Blind Pair (!000&;)' Pressed.
[14:58:06.748][I][RF_A:221]: TX: !000&;
[14:58:07.977][D][RF_A:303]: !DGZEnl;
[14:58:07.981][V][RF_A:313]: [HEX] 21 44 47 5A 45 6E 6C 3B
[14:58:17.258][D][button:023]: 'Blind Pair (!000&;)' Pressed.
[14:58:17.262][I][RF_A:221]: TX: !000&;
[14:58:17.588][D][RF_A:303]: !KV8A,RAD;
[14:58:17.592][V][RF_A:313]: [HEX] 21 4B 56 38 41 2C 52 41 44 3B
[14:58:18.460][D][RF_A:303]: !KV8vU22,RAD;
[14:58:18.464][V][RF_A:313]: [HEX] 21 4B 56 38 76 55 32 32 2C 52 41 44 3B
[14:58:20.301][D][RF_A:303]: !KV8r099b180,RAD;
[14:58:20.304][V][RF_A:313]: [HEX] 21 4B 56 38 72 30 39 39 62 31 38 30 2C 52 41 44 3B
[14:58:41.577][D][RF_A:303]: !KV8r092b000,RAD;
[14:58:41.581][V][RF_A:313]: [HEX] 21 4B 56 38 72 30 39 32 62 30 30 30 2C 52 41 44 3B
[14:58:44.321][D][RF_A:303]: !KV8r100b180,RAD;
[14:58:44.325][V][RF_A:313]: [HEX] 21 4B 56 38 72 31 30 30 62 31 38 30 2C 52 41 44 3B
[14:58:48.557][D][RF_A:303]: !KV8r087b000,RAD;
[14:58:48.561][V][RF_A:313]: [HEX] 21 4B 56 38 72 30 38 37 62 30 30 30 2C 52 41 44 3B
[14:58:54.311][D][RF_A:303]: !KV8r100b180,RAD;
[14:58:54.315][V][RF_A:313]: [HEX] 21 4B 56 38 72 31 30 30 62 31 38 30 2C 52 41 44 3B
[14:59:50.101][D][text:050]: 'Dooya Blind ID' - Setting text value: KV8
[14:59:50.104][D][text:016]: 'Dooya Blind ID': Sending state KV8
[14:59:50.189][D][button:023]: 'Blind Open (!ID o;)' Pressed.
[14:59:50.193][I][RF_A:221]: TX: !KV8o;
[14:59:50.380][D][RF_A:303]: !KV8o,RAD;
[14:59:50.384][V][RF_A:313]: [HEX] 21 4B 56 38 6F 2C 52 41 44 3B
[14:59:55.208][D][button:023]: 'Blind Stop (!ID s;)' Pressed.
[14:59:55.212][I][RF_A:221]: TX: !KV8s;
[14:59:55.382][D][RF_A:303]: !KV8s,RAB;
[14:59:55.385][V][RF_A:313]: [HEX] 21 4B 56 38 73 2C 52 41 42 3B
[14:59:56.240][D][RF_A:303]: !KV8r075b000,RAC;
[14:59:56.244][V][RF_A:313]: [HEX] 21 4B 56 38 72 30 37 35 62 30 30 30 2C 52 41 43 3B
[15:00:17.819][D][button:023]: 'Blind Close (!ID c;)' Pressed.
[15:00:17.823][I][RF_A:221]: TX: !KV8c;
[15:00:18.015][D][RF_A:303]: !KV8c,RAB;
[15:00:18.018][V][RF_A:313]: [HEX] 21 4B 56 38 63 2C 52 41 42 3B
[15:00:23.821][D][RF_A:303]: !KV8r100b180,RAA;
[15:00:23.825][V][RF_A:313]: [HEX] 21 4B 56 38 72 31 30 30 62 31 38 30 2C 52 41 41 3B
[15:01:22.179][D][RF_A:303]: !KV8r094b000,RAC;
[15:01:22.183][V][RF_A:313]: [HEX] 21 4B 56 38 72 30 39 34 62 30 30 30 2C 52 41 43 3B
[15:01:29.853][D][RF_A:303]: !KV8r100b180,RAC;
[15:01:29.857][V][RF_A:313]: [HEX] 21 4B 56 38 72 31 30 30 62 31 38 30 2C 52 41 43 3B
Using the pairing command i captured on the original firmware '!000v?; and using the p2 button on the remote for the shade it paired with the RF bridge
Two way comms, open close and Running (RAB) status and percentage.
TX: !KV8c; close pressed
!KV8c,RAB; running
!KV8r094b000,RAC; 94%
!KV8r100b180,RAC; 100% (using the remote and seeing feedback in the esp log)
With a bit more effort these Pulse2Hubs can be turned into an offline blind controller which does not rely on AWS or the cloud and improve reliability no end.