syssi / esphome-yeelight-ceiling-light

ESPHome custom firmware for some Yeelight Ceiling Lights
Apache License 2.0
141 stars 22 forks source link

Add yeelink.light.fancl5 support #42

Open 0neday opened 2 years ago

0neday commented 2 years ago

RT,

thank you

syssi commented 2 years ago

You could open the device and provide some photos. If you own a multimeter I could provide some instructions how to collect all required infos.

0neday commented 2 years ago

You could open the device and provide some photos. If you own a multimeter I could provide some instructions how to collect all required infos.

thank you, I have multimeter, I will give you some postion of gpio, such as gpio0, rx, tx ...

syssi commented 2 years ago

Some high-res photos of the mainboard especially the WiFi daughter board are important. In best case the pin header between the mainboard and the daughter board is labeled.

Are you able to control different speed levels of the ceiling fan using the app?

0neday commented 2 years ago

Are you able to control different speed levels of the ceiling fan using the app?

using Mijia app, I can control fan speed.

syssi commented 2 years ago

How many levels?

0neday commented 2 years ago

3 level speed, it use uart communicate between esp32 and bp8601

syssi commented 2 years ago

You could use a logic analyzer (something like this https://de.aliexpress.com/item/1005002511491492.html or https://de.aliexpress.com/item/1005003812410259.html) plus a open-source software called sigrok/pulseview to capture the serial traffic between the ESP and the driver board of the fan. As soon as we know the messages here we can try to replicate the communication. The ceiling lamp is working already? What's working and what's not if you flash your yaml? Did you make a backup of the stock firmware?

0neday commented 2 years ago

no, I have not flash esphome, I just test UART using a usb-ttl tools, and get

brownout detector 

when I set gpio0 = low level, it could enter into flash mode.

I can not get gpio for light., the gpio of my yaml is copy from your's, do not test.

Do you any decompiled circuit diagram of yeelight ceiling?

syssi commented 2 years ago

The brownout detector is triggered if the power supply is not strong enough. I assume you did power the ESP using the USB-TTL converter. Cheap converters (f.e. PL2303) doesn't provide enough current to power an ESP. You should/could use an external power supply.

If you are able to enter the flash mode please make a backup of the firmware. Please follow this guide:

https://github.com/mmakaay/esphome-xiaomi_bslamp2/blob/dev/doc/flashing.md#make-a-backup-of-the-current-firmware

I don't have any circuit diagrams. How to identify the PWM GPIOs:

  1. Power the ESP32 properly. You must be able to control the device using your Android app. It's fine if you power the ESP only.
  2. Connect the USB-to-TTL module to RX, TX and GND. You should see debug messages of the stock firmware
  3. Turn the light on and change the color temperature to 100% warm-white
  4. Measure the voltage between GND and every GPIO. You should find a GPIO providing 3.3V (PWM=100%).
  5. Set the warm white brightness to 50%. Measure again. You should find a GPIO providing 1.5-2V now (PWM=50%).
  6. Set the warm white brightness to the lowest value (1%?). Measure again.

Follow the same steps to identify the cold-white PWM GPIO and the Night Light PWM GPIO. It's possibe there is a feature called "power supply standby" (STB). This is a GPIO which drives/turns on the power supply if you turn on the light (Voltage between GND and STB should be 3.3V). If you turn off the light the voltage should drop to 0V.

Please try to identify these GPIOs so you are able to control the ceiling light. The next step will be the reverse engineering of the fan. I'm afraid a logic analyzer is required here. I could provide some guidance how to use sigrok to decode the serial traffic between the ESP and the driver board of the fan.

syssi commented 2 years ago

Another wish/suggestion:

  1. Please use a continuity tester or a ohm meter to measure the continuity between the test points (TP1, TP5) and the GPIOs of the ESP. I bet GPIO0 is exposed as test point.
  2. Use a continuity tester or a ohm meter to measure the continuity between CON2 (connector 2) of the fan driver board (EN, WIFI, TXD, RXD, (GND)) and the GPIOs of the ESP.
  3. Could you provide the label / model identifier of IC2? I assume it's an EEPROM and connected via I2C (2 GPIOs, VCC, GND). If we know the type we can lookup the pinout and identify the SDA and SCL pins connected to the ESP.
  4. There is a piezo buzzer labeled with BP1. I assume the buzzer is somehow connected to the ESP. Usually a piezo isn't connected directly to the ESP. I assume there is an NPN transistor in between. Do you know how to let the device beep?
  5. Please provide the type / model identifier of the 24 pin IC located at the fan driver board. I've trouble to identify the label.
0neday commented 2 years ago

Measure the voltage between GND and every GPIO. You should find a GPIO providing 3.3V (PWM=100%).

you are genius!

Do you know how to let the device beep

when use remote controller and tap any key, the buzzer beep a time.

label / model identifier of IC2 provide the type / model identifier of the 24 pin IC located at the fan driver board

I will provide the label of IC, wait some time

0neday commented 2 years ago

test result is :

cold --> gpio21
warn --> gpio19
night mode ---> gpio23

so, the light gpio is same as yeelink.light.ceiling15

ps: I have question is except for fan, there is a remote controller, that is connected by bluetooth.

syssi commented 2 years ago

Could you provide a photo of your remote control? I assume it's the standard BLE remote talking to the ESP. It's known how to receive commands from these kind of remotes but we shouldn't try to support the remote control at the first iteration. I would be happy if we focus on the ceiling light and fan feature.

syssi commented 2 years ago

Did you backup the stock firmware?

0neday commented 2 years ago

Did you backup the stock firmware?

yes, I have

syssi commented 2 years ago

The remote control is the standard bluetooth remote control of yeelight fans. I assume it's the 0x068E: "YLYK01YL-FANCL". If you want to receive commands using Home Assistant you could use this custom component: https://github.com/custom-components/ble_monitor/

syssi commented 2 years ago

It looks like you managed to flash the ESP32. If you don't want to buy a logic analyzer I could provide some instruction how to piggy back another ESP (running esphome to dump some traffic) to the serial line between the ESP and the fan driver board. The idea is to sniff the traffic between these two components to get a better understanding how to replicate the communication.

0neday commented 2 years ago

back another ESP (running esphome to dump some traffic) to the serial line between the ESP

that is a good idea! I have a nodemcu esp32 , I will to flash stock firmware into it , and check what happed

0neday commented 2 years ago

the buzzer beep is for fan mcu BP6601, check here https://bbs.elecfans.com/jishu_1868688_1_1.html

0neday commented 2 years ago

you want to receive commands using Home Assistant you could use this custom component

nop, I want to use esphome to execute animation.

I use ble tracker to get remote controller adversie data, like

# for fan
[21:53:04][D][ble_adv:107]:     - 0xFE95: (length 17)  - 50.30.8E.06.8B.0B.85.6E.38.C1.A4.01.10.03.00.00.00 (17)
# for light
[21:52:09][D][ble_adv:107]:     - 0xFE95: (length 17)  - 50.30.8E.06.89.0B.85.6E.38.C1.A4.01.10.03.01.00.00 (17)
# for recirculating air
[21:54:25][D][ble_adv:107]:     - 0xFE95: (length 17)  - 50.30.8E.06.8E.0B.85.6E.38.C1.A4.01.10.03.02.00.00 (17)
....

so, just use esphome to get adversie data and using xor get third-to-last byte and compare, and using on_... to execute .

syssi commented 2 years ago

In the past the single core ESP32 of the yeelight devices did crash if you try to use the BLE tracker at your configuration yaml. Some remote controls are encrypted and the bind_key is required to decrypt the data. Do you know my implementation of the ceiling lamp remote control?

https://github.com/syssi/esphome-yeelight-ceiling-light/blob/main/yeerc_ylyk01yl.yaml

This implementation can be easily extended to support the yeerc_fancl remote control. Please keep in mind: A dual core ESP32 is required to avoid the boot loop.

I've replaced the single core ESP32 of some of my lights to be able to use bluetooth without trouble.

0neday commented 2 years ago

single core ESP32 of the yeelight devices did crash

I know this issue, but it could be fixed by add delay vTaskDelay(10/portTICK_PERIOD_MS); on_loop to avoid loop reboot

esphome:
  name: yeelight-c900
  platformio_options:
    platform_packages:
     - framework-arduinoespressif32 @ https://github.com/pauln/arduino-esp32.git#solo-no-mac-crc/1.0.6

  on_loop:
   - lambda: |
        vTaskDelay(10/portTICK_PERIOD_MS);

I use framework is - framework-arduinoespressif32 @ https://github.com/pauln/arduino-esp32.git#solo-no-mac-crc/1.0.6 that support OTA web.

syssi commented 2 years ago

I know this issue, but it could be fixed by add delay vTaskDelay(10/portTICK_PERIOD_MS); on_loop to avoid loop reboot

Wow. Good job! I didn't know about this solution.

I use framework is - framework-arduinoespressif32 @ https://github.com/pauln/arduino-esp32.git#solo-no-mac-crc/1.0.6 that support ota web.

I prefer ESP-IDF over the Arduino framework. I flash my esphome nodes using esphome run config.yaml + ota: component.

syssi commented 2 years ago

the buzzer beep is for fan mcu BP6601, check here https://bbs.elecfans.com/jishu_1868688_1_1.html

Could you help me to download provide the two attachments?

0neday commented 2 years ago

maybe, we could use decompile engineering to check how control fan https://github.com/jsandin/esp-bin2elf

syssi commented 2 years ago

I've no experience with reverse engineering of ELF executables. This is the way I would choose:

  1. Identify the GPIOs attached to con2 the pin header between the mainboard and the fan driver board.
  2. Flash the backup of the stock firmware to another ESP32. Try to boot. It's likely the ESP doesn't boot/crashs because peripherals (EEPROM?) isn't available or the MAC address doesn't match. If the ESP boots connect the USB-TTL converter to the RX/TX pins of the con2 GPIOs. Use the android app to start/stop/control the fan. Try different baudrates. Do you see some messages/control instructions?
  3. If step 2 isn't successful flash the stock firmware back to the yeelight single core ESP32. Let the ESP boot and sniff the traffic at con2 again without having the fan driver board attached.

Some more ideas:

  1. Attach dhe fan driver board using con2 to the ESP32 again and power the board with 5V. Don't apply the 24V. I hope this doesn't harm the device but I don't know for sure. You could attach the USB-TTL converter to the RX/TX lines of con2. Alternatively you could use another ESP (f.e. ESP8266) plus a configuration yaml like this

https://github.com/syssi/esphome-jbd-bms/blob/main/tests/esp8266-dummy-receiver.yaml

to sniff the traffic of one direction. Try to identify the baudrate. Capture the traffic/commands from the ESP. Swap the RX/TX lines and capture the responses from the fan driver board (hopefully the board responds). If this doesn't work I would attach a 24V power source too to make the fan driver board happy.

0neday commented 2 years ago

Identify the GPIOs attached to con2 the pin header

I try to identify, but failed! the board have a layer of insulating glue.

You could attach the USB-TTL converter to the RX/TX lines of con2.

bewteen rx, tx of con2 and gpio, there is some divider resistor, the voltage rx and tx is 2V

0neday commented 2 years ago

Do you know my implementation of the ceiling lamp remote control?

I have check your external_components, but I have no idea how to get it work.

I use

# BLE    
esp32_ble_tracker:
  on_ble_advertise:
    - mac_address: A4:C1:38:6E:85:0B
      then:
        - lambda: |-
            for (auto data : x.get_service_datas()) {
                ESP_LOGD("ble_adv", "    - %s: (length %i)  - %s", data.uuid.to_string().c_str(), data.data.size(),hexencode(data.data).c_str());
            }

to print adv data. I confirm there is no bind_key and no encryption data.

and then, use x[14] to get key code, and use if condition select

# BLE    
esp32_ble_tracker:
  on_ble_service_data_advertise:
    - mac_address: A4:C1:38:6E:85:0B
      service_uuid: FE95
      then:
         - if:
            condition: 
              lambda: "return(x[14] == 1);"
            then:
             - light.turn_on:
                id: ceiling_light

         - if:
            condition: 
              lambda: "return(x[14] == 4);"
            then:
             - light.turn_on:
                id: night_light

and now, I have a question is when I tap one time, but on_ble_service_data_advertise advertise 2 times data, so when I use light.toggle, that work poorly

0neday commented 2 years ago

Identify the GPIOs attached to con2 the pin header between the mainboard and the fan driver board.

comfirm


gpio16( RX )  ---> con2 ( TX )
gpio17( TX ) ----> con2 ( RX )
EN( pin3 )  ----> con2( wifi_en )
gpio33  --->  fan_buzzer_beep
syssi commented 2 years ago

bewteen rx, tx of con2 and gpio, there is some divider resistor, the voltage rx and tx is 2V

I assume the fan driver board runs at 5V and the logic level of the serial interface of the BP6601 is 5V too. To shift the logic level to 3.3V (because the ESP isn't 5V tolerant) the voltage divider is used. You could measure the voltage between TX and GND at the BP6601 to prove this assumption.

syssi commented 2 years ago

when I tap one time, but on_ble_service_data_advertise advertise 2 times data, so when I use light.toggle, that work poorly

There is a counter at the frame which is increased per button press:

https://github.com/syssi/esphome-yeelight-ceiling-light/blob/main/components/xiaomi_ble/xiaomi_ble.cpp#L154

The external component tracks the counter value and discards duplicates. I'm busy today and will provide some more input how to improve and use the external component tomorrow.

syssi commented 2 years ago

How to use / extend the external component:

This instructions clones the remote repository every time you compile your configuration:

external_components:
  - source: github://syssi/esphome-yeelight-ceiling-light@main
    refresh: 0s

To be able to make local changes you have to clone the repository manually:

git clone git@github.com:syssi/esphome-yeelight-ceiling-light.git

And let the external component configuration point to the local checkout / directory:

external_components:
  - source: components
    refresh: 0s

Now you can make changes here:

components/xiaomi_ble/xiaomi_ble.cpp

We need a new block for your device like this:

  } else if ((raw[2] == 0x53) && (raw[3] == 0x01)) {  // Yeelight Remote Control YLYK01YL
    result.type = XiaomiParseResult::TYPE_YLYK01YL;
    result.name = "YLYK01YL";

Your device type idenfier is 0x068E. As next step (I assume) we need a new directory for the device implementation. May be your remote control is pretty similar to the YLYK01YL. In this case we could just extend the existing implementation. This diff should help to get a better understanding how a full device implementation looks like:

https://github.com/esphome/esphome/pull/1783/files#diff-a1fc96aecdd718ce2fd8d4892c7d9ecef75683f467cc58282fd4c0b13cd24934

syssi commented 2 years ago

Could you provide the bluetooth name of your remote control? I assume it's advertised and part of your ESPHome log at the very beginning.

syssi commented 2 years ago

I assume this GPIO must be enable/pulled to start/enable the fan driver board:

EN( pin3 )  ----> con2( wifi_en )
0neday commented 2 years ago

Could you provide the bluetooth name of your remote control? I assume it's advertised and part of your ESPHome log at the very beginning.

yee-rc

0neday commented 2 years ago

The external component tracks the counter value and discards duplicates. I'm busy today and will provide some more input how to improve and use the external component tomorrow.

have a good day, thank you very much

syssi commented 2 years ago

yee-rc

Is there a model identifier on the back (under the sticker?) or somewhere?

0neday commented 2 years ago

when I tap one time, but on_ble_service_data_advertise advertise 2 times data, so when I use light.toggle, that work poorly

There is a counter at the frame which is increased per button press:

https://github.com/syssi/esphome-yeelight-ceiling-light/blob/main/components/xiaomi_ble/xiaomi_ble.cpp#L154

The external component tracks the counter value and discards duplicates. I'm busy today and will provide some more input how to improve and use the external component tomorrow.

thank you, I have fixed by defined global variable frame_counter and use if condition and

globals:
   - id: frame_counter
     type: int
     initial_value: '0'

# BLE    
esp32_ble_tracker:
  on_ble_service_data_advertise:
    - mac_address: A4:C1:38:6E:85:0B
      service_uuid: FE95
      then:
         - if:
            condition: 
              lambda: "return(x[14] == 1 && id(frame_counter) != x[4]);"
            then:
             - lambda: |-
                 id(frame_counter) = x[4];
                 id(yeelight_remote_controller).publish_state(x[14]);
             - switch.turn_on: buzzer_beep
             - light.toggle: ceiling_light

         - if:
            condition: 
              lambda: "return(x[14] == 4 && id(frame_counter) != x[4]);"
            then:
             - lambda: |-
                 id(frame_counter) = x[4];
                 id(yeelight_remote_controller).publish_state(x[14]);
             - switch.turn_on: buzzer_beep
             - light.toggle: night_light
0neday commented 2 years ago

yee-rc

Is there a model identifier on the back (under the sticker?) or somewhere?

no any identifier, maybe is a DIY product, just know the chip of ble is TLSR8267

0neday commented 2 years ago

sniff uart communication data fromat is

[D][uart_debug:114]: <<< 01:01:01:13:11:03 # off
[D][uart_debug:114]: <<< 01:03:01:68:64:03 # on

# Natural wind third gear
[D][uart_debug:114]: <<< 01:03:01:05:01:03
[D][uart_debug:114]: <<< 01:03:01:36:32:03
[D][uart_debug:114]: <<< 01:03:01:68:64:03
syssi commented 2 years ago

You can send these frames using uart.write: See https://github.com/syssi/esphome-jbd-bms/blob/main/tests/esp8266-query-data.yaml

0neday commented 2 years ago

thank you! fan is work.

syssi commented 2 years ago

Could you try to provide a much details about the frames as possible?

Byte  Value  Description
0     0x01   Start of frame (always 0x01)
1     0x03   0x01=Off, 0x03=On (is 0x02 also a possible value?)
2     0x01   ??
3     0x68   Fan speed?
4     0x64   Checksum (n - byte1 - byte2 - byte3)
5     0x03   End of frame (always 0x03)

Does the uC of the fan driver board respond? The dummy_receiver should write the answers to the log.

syssi commented 2 years ago

It's possible byte3 is the checksum: sum of byte1+byte2+byte4.

0neday commented 2 years ago

Could you try to provide a much details about the frames as possible?

Byte  Value  Description
0     0x01   Start of frame (always 0x01)
1     0x03   0x01=Off, 0x03=On (is 0x02 also a possible value?)
2     0x01   ??
3     0x68   Fan speed?
4     0x64   Checksum (n - byte1 - byte2 - byte3)
5     0x03   End of frame (always 0x03)

Does the uC of the fan driver board respond? The dummy_receiver should write the answers to the log.

new data is here, https://github.com/0neday/yeelight-c900#sniff-communication-esp32-with-bp6601

0x04 is on, 0x03 reverse 

this need more data to recompile frame format. because I flash stock firmware into a new esp32, that cannot connect Mijia server( maybe server use mac address check ) , so Mijia App show my device is offline. so that I can not test 0-100% speed.


# open
[D][uart_debug:114]: >>> 01:04:01:18:13:03
[D][uart_debug:114]: <<< 01:F3:01:07:13:03

# close
[D][uart_debug:114]: >>> 01:01:01:13:11:03
[D][uart_debug:114]: <<< 01:F3:01:05:11:03

# 1 level
[D][uart_debug:114]: >>> 01:03:01:05:01:03
[D][uart_debug:114]: <<< 01:F3:01:F5:01:03

# 2 level
[D][uart_debug:114]: >>> 01:03:01:36:32:03
[D][uart_debug:114]: <<< 01:F3:01:26:32:03

# 3 level 
[D][uart_debug:114]: >>> 01:03:01:68:64:03
[D][uart_debug:114]: <<< 01:F3:01:58:64:03
syssi commented 2 years ago

I still assume there is an eeprom next to the ESP32 containing some secrets required to talk to the Mijia cloud.

syssi commented 2 years ago
# on
[D][uart_debug:114]: >>> 01:04:01:18:13:03 -> 0x04 + 0x01 + 0x13 = 0x18
[D][uart_debug:114]: <<< 01:F3:01:07:13:03 -> 0xF3 + 0x01 + 0x13 = 0x07

# off
[D][uart_debug:114]: >>> 01:01:01:13:11:03 -> 0x01 + 0x01 + 0x11 = 0x13
[D][uart_debug:114]: <<< 01:F3:01:05:11:03 -> 0xF3 + 0x01 + 0x11 = 0x05

# 1 level
[D][uart_debug:114]: >>> 01:03:01:05:01:03 -> 0x03 + 0x01 + 0x01 = 0x05
[D][uart_debug:114]: <<< 01:F3:01:F5:01:03 -> 0xF3 + 0x01 + 0x01 = 0xF5

# 2 level
[D][uart_debug:114]: >>> 01:03:01:36:32:03 -> 0x03 + 0x01 + 0x32 = 0x36
[D][uart_debug:114]: <<< 01:F3:01:26:32:03 -> 0xF3 + 0x01 + 0x32 = 0x26

# 3 level 
[D][uart_debug:114]: >>> 01:03:01:68:64:03 -> 0x03 + 0x01 + 0x64 = 0x68
[D][uart_debug:114]: <<< 01:F3:01:58:64:03 -> 0xF3 + 0x01 + 0x64 = 0x58

Byte  Value  Description
0     0x01   Start of frame (always 0x01)
1     0x03   0x01=Off, 0x03=On, 0x04=OnReverse
2     0x01   ??
3     0x68   Checksum (byte1 + byte2 + byte4)
4     0x64   Fan speed 0x01...0x64 = 1...100%
5     0x03   End of frame (always 0x03)
syssi commented 2 years ago

Could you try to send an command with an invalid checksum and/or invalid speed (0x65)? I assume the first nibble of 0xF3 indicates success. May be byte 2 of the frame is static and always 0x01.

0neday commented 2 years ago

# 0x04,  with error chechsum, fan not work
[D][uart_debug:114]: >>> 01:04:01:17:13:03
[D][uart_debug:114]: <<< 01:F3:01:94:A0:03

# 0x04, change speed to 0x01 , no return 
[D][uart_debug:114]: >>> 01:04:01:06:01:03

# 0x03, speed = 0x65 is work
[D][uart_debug:114]: >>> 01:03:01:69:65:03
[D][uart_debug:114]: <<< 01:F3:01:59:65:03

# 0x03, speed =  0x20 , work
[D][uart_debug:114]: >>> 01:03:01:24:20:03
[D][uart_debug:114]: <<< 01:F3:01:14:20:03

# 0x03 , with error checksum, fan not change state
[D][uart_debug:114]: >>> 01:03:01:15:10:03
[D][uart_debug:114]: <<< 01:F3:01:94:A0:03

I guess speed change is just for `0x03`, 
so byte 1 is identify of 自然风 or 循环风 ?

`0x04` is for `on`

the `A0` of retrun code say, not correct, or chechsum error.
0neday commented 2 years ago

I have another question is how to use uart read to get return code for comfirming the write is correct. like use uart.read get return, if get byte 4 is A0, that show write is error.