From 702f24751a7b23ec8ddbc107c17fefe0fe2cd561 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Sun, 18 Apr 2021 13:23:34 +0200 Subject: [PATCH] Terminology consistency and preparing documentation for stable release. --- README.md | 147 ++++++--------------------- __init__.py | 2 +- doc/FLASHING.md | 189 ----------------------------------- doc/configuration.md | 3 + doc/example.yaml | 40 ++++---- doc/flashing.md | 197 +++++++++++++++++++++++++++++++++++++ doc/installation.md | 59 +++++++++++ doc/known_issues.md | 83 ++++++++++++++++ doc/why_custom_firmware.md | 37 +++++++ front_panel_hal.h | 9 +- output/output.h | 2 +- sensor/slider_sensor.h | 4 +- 12 files changed, 439 insertions(+), 333 deletions(-) delete mode 100644 doc/FLASHING.md create mode 100644 doc/configuration.md create mode 100644 doc/flashing.md create mode 100644 doc/installation.md create mode 100644 doc/known_issues.md create mode 100644 doc/why_custom_firmware.md diff --git a/README.md b/README.md index 13df3ac..5266bb5 100644 --- a/README.md +++ b/README.md @@ -1,126 +1,39 @@ -# esphome-xiaomi_bslamp2 +# ESPHome component for Xiaomi Mijia Bedside Lamp 2 -## Warning: this code is still under development +The Bedside Lamp 2 is a smart RGBWW LED lamp, produced by Yeelight. It can +be controlled via the WiFi network and using a touch panel on the front of +the device. The touch panel contains a power button, a button that changes +the color of the light and a slider that can be used to change the +brightness of the light. -This code might not yet be production-ready. -At this point, it is declared beta-quality. +This project provides a custom component for ESPHome, which makes it +possible to fully control every aspect of the lamp. -The new firmware is currently being tested and depending on the actual -use cases and issues that we might run into, breaking changes could still -occur at some point. +## Quick start guide +For those who have experience with flashing ESPHome onto devices, here's a +quick start guide: -## Is it safe to already install this firmware on my device? +* Clone the [GitHub repo](https://github.com/mmakaay/esphome-xiaomi_bslamp2) + into your ESPHome `config/custom_components` directory. +* Copy `config/custom_components/xiaomi_bslamp2/doc/example.yaml` to + `config/your_device_name.yaml` and modify it to your needs. +* Compile the `firmware.bin` file and download it to the device to which you + have connected a serial to USB adapter (FTDI). +* Open up the lamp and connect its `TX`, `RX`, `GND` and `GPIO0` debug pads + to the serial adapter (see the [detailed installation + guide](doc/install.md) for the debug pad locations). +* Power up the lamp with `GPIO0` connected to GND to enable flashing mode. +* Flash `firmware.bin` onto the device. -As long as you keep a backup of the original firmware, this is quite safe :-) -I have two lamps that both are running the latest development firmware and -they are functioning very well. +If you experience regular disconnects between Home Assistant and the lamp, +then take a look at the [known issues document](doc/known_issues.md). -I sometimes see API disconnection issues, but those can all be traced back -to the underlying frameworks. For the most prevalent issue, I did some -debugging and wrote a fix (it is mentioned below). +## Table of contents: -For each commit of the code, I will do my best to commit it in a working -state. Once a first completed stable release is cooked up, I will tag -production releases of the code to make it easier to pick the safe version -for production purposes. +* [Why custom firmware?](doc/why_custom_firmware.md) +* [Installation guide](doc/installation.md) +* [Configuration_guide](doc/configuration.md) +* [Flashing guide](doc/flashing.md) +* [Known issues](doc/known_issues.md) - -## Installation - -The code must be compiled using ESPHome. Therefore, a prerequisite is that -you have ESPHome up and running in some form (command line, docker container, -web dashboard, possibly from within Home Assistant). -For information on this, please refer to the documentation on the -[ESPHome website](https://esphome.io). - -Create a folder named `custom_components` in the folder where your device's -yaml configuration file is stored. Then clone the the Github repo into a -subfolder `xiaomi_bslamp2`. For example on the command line: - -``` -# mkdir custom_components -# cd custom_components -# git clone https://github.com/mmakaay/esphome-xiaomi_bslamp2 xiaomi_bslamp2 -``` - -Your folder structure should now look like: -``` -config -├── yourdevice.yaml -├── custom_components/ -│ ├── xiaomi_bslamp2/ -│ . ├── README.md -. . ├── LICENSE.md -. . . -``` - -On a Rapsbery Pi with HomeAssistant and ESPHome as a plugin, the directory -should be: `/config/esphome/custom_components/xiaomi_bslamp2/` - -``` -config -├── esphome -│ ├── yourdevice.yaml -│ ├── custom_components/ -| . ├── xiaomi_bslamp2/ -│ . . ├── README.md -. . . ├── LICENSE.md -. . . . -``` - -Then create the required configuration in your device's yaml configuration -file. For an example file, take a look at the example file -[doc/example.yaml](doc/example.yaml) in this repository. - -After these steps you can compile your firmware `firmware.bin` file. -This firmware can then be flashed onto the device. - -Like normal with ESPHome, the first time you will have to flash the -device using a serial interface. After this initial flashing operation, you -can flash new versions of the firmware using the OTA (Over The Air) method. - -See [doc/FLASHING.md](doc/FLASHING.md) for hints on opening up the device and -flashing its firmware using the serial interface. - - -## Issue: the device keeps losing its connection to Home Assistant - -This is not a problem with the device or the custom firmware, but a problem -in the upstream library "AsyncTCP". I did identify an issue and my pull -request for a fix was accepted and merged: - - https://github.com/OttoWinter/AsyncTCP/pull/4 - -This fix will likely be available in the next release of ESPHome -(the current version at the time of writing is 1.16.2). -If you want to try out this fix on beforehand, then create a `libs` folder -in the folder where your device's yaml configuration file is stored (e.g. -when using the Home Assistant plugin: `/config/esphome/libs/`). -Then clone the following repository into that folder: - - https://github.com/OttoWinter/AsyncTCP - -For example on the command line: - -``` -# cd /config/esphome -# mkdir libs -# cd libs -# git clone https://github.com/OttoWinter/AsyncTCP -``` - -Then add a pointer to this folder from within your device's yaml -configuration file, using the `lib_extra_dirs` option. Provide it with the -absolute path to your `libs` folder. The relevant part of the config change -looks like this (matching the example from above): - -```yaml -esphome: - platformio_options: - lib_extra_dirs: /config/esphome/libs -``` - -This way, the repository version of the library will override the version of -the library that is bundled with ESPHome. Build the device firmware and -flash the device like you would normally do. diff --git a/__init__.py b/__init__.py index 6e8b8a7..f14cc63 100644 --- a/__init__.py +++ b/__init__.py @@ -122,7 +122,7 @@ def make_front_panel_hal(config): cg.add(fp_hal.set_i2c_address(config[CONF_ADDRESS])) def to_code(config): - # Dirty little hack to make the ESPHome component loader inlcude + # Dirty little hack to make the ESPHome component loader include # the code for the "gpio" platform for the "output" domain. # Loading specific platform components is not possible using # the AUTO_LOAD feature unfortunately. diff --git a/doc/FLASHING.md b/doc/FLASHING.md deleted file mode 100644 index 3f7c9d9..0000000 --- a/doc/FLASHING.md +++ /dev/null @@ -1,189 +0,0 @@ -# Flashing the Xiaomi Mijia Bedside Lamp 2 - -## Tools needed - -* Allen key (2mm, 5/64") or torx (T8) screw driver -* Soldering Iron -* Perhaps some sticky tape -* A serial to USB adapter (FTDI) -* Some wires - -## Warning - -We have writen these instructions with care, but we will give absolutely no -warranty. Perhaps you will destroy your device and your computer. - -## Opening the lamp, to expose the PCB - -Remove the rubber pads from the botton of the lamp. - -Unbolt the 4 screws which were hidden by the rubber pads. - -![Photo of the screws](images/screws.jpg "Use an allen key or torx screw driver to remove the screws.") - -Remove the bottom from the device, exposing the PCB. -This might take a bit of force. Just pull it up bit by bit until it pops loose. - -For some good pictures of disassembling this device, take a look -[at this blog post](https://mysku.ru/blog/china-stores/78455.html) -It is in Russian, but the translation by Google works well and moreover, the -pictures are the most important thing here. If you scroll down, you will -find them easily. - -## Solder the wires - -Many of the serial to USB adapter have some header pins to which you can -connect the wires of a device. Therefore, I find it most useful to take some -dupont wires with a female end to them, and cut off the other end. Strip the -wire on the other and, and then it can be used to solder it to the board. - -Solder the wires to the RX, TX, GND and GPIO0 debug pads that are shown in this -photo. It is not required to solder a wire to the 3.3V debug pad. This pad -is not directly connected to the 3.3V Vin of the ESP32 chip, making it a -less than optimal candidate for powering the board during flashing. Instead, -powering the lamp using its own power supply works best. - -![Soldering points](images/Soldering_points.jpg) - -You can use some sticky tape to fixate the cables before soldering. - -## Connect the wires to your serial to USB device - -The wires must be connected as follows: - - | Soldering point| Serial USB Adapter name | - | -------------- |:------------------------:| - | GND | GND | - | TX | RX | - | RX | TX | - | GPIO0 | GND | - -To be able to flash the device, GPIO0 must be connected to ground while the -device is booted. Therefore, connect these wires before plugging in the -device's power adapter. Flashing will not work if you connect these wires -when the device has already been powered up. - -## When you only have one GND pin on your USB Adapter - -If your USB Adapter does not have multiple GND pins, then you'll have -to find another way to attach GPIO0 to ground. Some options: - -- Use a breadbord, so you can connect the USB Adapter GND pin to a row on - the bread bord, and connect the GND and GPIO0 wires of the board to that - same row. - -- Solder a button on the board that connects GPIO0 to GND when pressed. Then - you can hold down this button while plugging in the device's power supply. - After booting up, you can release the button (the serial console will also - mention that the device is now in flash mode). This is not the most - practical solution for most people (since only one such flash operation is - needed, from then on OTA - Over The Air - updates are possible), but it - was a great help to me during the initial reverse engineering and firmware - development. - -- Manually hold a wire connected to both a GND surface (e.g. the silver pad - on the left of the board) and the GPIO0 debug pad, while plugging in the - power supply. After booting, the wire can be removed. This is very fiddly - way of doing it, but I did it a few times and it can be done. - -- You could opt for temporarily soldering a lead between GND and GPIO0 on - the board, making GPIO0 pulled to ground permanently. It is a bit less - flexible than some other options, but if you only need to do the initial - backup and firmware flash of the device, then this can bee all that you - need. Remove the lead after flashing is done, otherwise the device won't - boot in normal mode. - -## Download and install esptool - -See: https://github.com/espressif/esptool/blob/master/README.md#installation--dependencies - -## Make a backup of the current firmware - -Here's an example on how to backup the original firmware from Linux. First, -unplug your device's power supply, then start the esptool read_flash command: - -``` -python esptool.py -p /dev/ttyUSB0 read_flash 0x0 0x400000 original-firmware.bin -``` - -/dev/ttyUSB0 is the port of the usb adaper on Linux. You can find what port -is used by the adapter by running `dmesg` after plugging in the USB device. -On Windows this is often `COM1`, `COM2` or `COM3`. - -Now plug in the power supply. The output of esptool should now show that it -connects to the device and downloads the firmware from it. - -**Caution**: You will find the WLAN SSID and Password of the latest used WiFi in -this file. Therefore, keep this backup in a safe place. - -## Restore the backed up firmware - -In case you need to rollback to the device's original firmware at some point, -here's an example of how to restore the original firmware from Windows, by fully -flashing it back onto the device. - -First, unplug your device's power supply, then start the esptool write_flash command: - -``` -python.exe .\esptool.py --chip esp32 --port COM3 --baud 115200 write_flash 0x00 original-firmware.bin -``` -Make sure that GPIO0 is connected to GND and plug in the power supply. -The output of esptool should now show that it connects to the device and uploads -the firmware to it. Be patient after the upload reaches 100%. The output is -silent for a while, but esptool tool is verifying if the firmware was uploaded -correctly. - -After the firmware was uploaded, unplug the power, disconnect GPIO0 from GND and -reconnect the power to boot into the restored firmware. - -## Flash new firmware - -Setup an ESPHome Project (see [README.md](../README.md)),compile the firmware -for the device and download the `firmware.bin` file to the device to which -the serial adapter is connected. - -You can flash the device using esphome or esptool. -I normally use the [esphome-flasher](https://github.com/esphome/esphome-flasher) -tool, which is a very easy to use GUI utility app for flashing ESPHome devices: - -- In the app, select the COM port of your serial adapter -- Also select the firmware.bin file to flash onto the device -- Power up the device with GPIO0 connected to GND -- Click the "Flash ESP" button to flash the firmware. - -If you want to flash with esptool, you can use: - -``` -python.exe .\esptool.py --chip esp32 --port COM3 --baud 115200 write_flash 0x1000 -``` - -After flashing, power down the device, disconnect GPIO0 from GND and reconnect -the power to boot into the ESPHome firmware. - -From here on, it is possible to flash the device OTA (over the air, which -means that the firmware is uploaded over WiFi) from ESPHome. Therefore, it -is now time to tuck away or remove those soldered wires and add the bottom -cover back on. - -## Troubleshooting flash - -If you have **A fatal error occurred: MD5 of file does not match data in -flash!**, then make sure you are powering the board using the device's own -power adapter. We've seen these errors when trying to power the board using -the 3.3V debug pad. - -After seeing this error, user @tabacha was able to successfully flash his -device using the regular power adapter and the tasmota boot loader using -the following command: - -``` -python esptool.py --chip esp32 -p /dev/ttyUSB0 --baud 115200 --before default_reset --after hard_reset write_flash -z --flash_mode dout --flash_freq 40m --flash_size detect 0x1000 bootloader_dout_40m.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 ~/Downloads/schlafzimmerbedlight.bin -``` - -You will find the missing tasmota boot files here: -https://github.com/arendst/Tasmota/tree/firmware/firmware/tasmota32/ESP32_needed_files - -*Note: user @tabacha was not able to use tasmota with the Bedside Lamp 2.* - -(remember that the [esphome-flasher](https://github.com/esphome/esphome-flasher) -will give you a bit less of a hard-core experience during flashing) diff --git a/doc/configuration.md b/doc/configuration.md new file mode 100644 index 0000000..51c0970 --- /dev/null +++ b/doc/configuration.md @@ -0,0 +1,3 @@ +# Configuration guide + +TODO diff --git a/doc/example.yaml b/doc/example.yaml index e92a90b..2e91cb1 100644 --- a/doc/example.yaml +++ b/doc/example.yaml @@ -7,9 +7,9 @@ substitutions: friendly_name: Bedside Lamp transition_length: 500ms - # Derive component identifiers, based on the device name. + # Derive component identifiers, based on the name. id_light: ${name} - id_front_panel_output: ${name}_front_panel_output + id_front_panel_illumination: ${name}_front_panel_output id_power_button: ${name}_power_button id_color_button: ${name}_color_button id_slider_level: ${name}_slider_level @@ -36,11 +36,11 @@ api: ota: password: "Password-For-Flashing-This-Device-Over-The-Air" -# The log level can be raised when needed for debugging the device. For -# production, a low log level is recommended. Mainly because high volume -# log output might interfere with the API/WiFi connection stability. -# So when raising the log level, beware that you might see dropped -# connections from Home Assistant and the network log viewer. +# The log level can be raised when needed for debugging the firmware. For +# production, a low log level is recommended. Mainly because high volume log +# output might interfere with the API/WiFi connection stability. So when +# raising the log level, beware that you might see dropped connections from +# Home Assistant and the network log viewer. logger: level: WARN @@ -51,7 +51,7 @@ logger: # Special platform + package are used for enabling unicore and disabling the # efuse mac crc check. These two changes are required for the ESP32-WROOM-32D -# chip that is used in the device. +# chip that is used in the lamp. esphome: name: ${name} platform: ESP32 @@ -61,13 +61,13 @@ esphome: platform_packages: |-4 framework-arduinoespressif32 @ https://github.com/pauln/arduino-esp32.git#solo-no-mac-crc/1.0.6 +# This component controls the LED lights of the lamp. light: - # This component controls the LED lights of the device. - platform: xiaomi_bslamp2 id: ${id_light} name: ${friendly_name} RGBW Light default_transition_length: ${transition_length} - # When the brightness is changed, then update the level indication + # When the brightness is changed, then update the level indicator # on the front panel accordingly. In night light mode, turn off # the front panel illumination. on_brightness: @@ -78,11 +78,11 @@ light: state: night then: - output.set_level: - id: ${id_front_panel_output} + id: ${id_front_panel_illumination} level: 0 else: - output.set_level: - id: ${id_front_panel_output} + id: ${id_front_panel_illumination} level: !lambda return x; # You can use any effects that you like. These are just examples. effects: @@ -117,25 +117,27 @@ light: # This text sensor propagates the currently active light mode. # The possible light modes are: "off", "rgb", "white" and "night". +# By setting the name, the text_sensor will show up as an entity +# for the lamp in Home Assistant. text_sensor: - platform: xiaomi_bslamp2 name: ${name} Light Mode id: ${id_light_mode} -# This output controls the front panel illumination + level indication. +# This float output controls the front panel illumination + level indicator. # Value 0.0 turns off the illumination. # Other values (up to 1.0) turn on the illumination and set the level -# indication to the requested level. +# indicator to the requested level. output: - platform: xiaomi_bslamp2 - id: ${id_front_panel_output} + id: ${id_front_panel_illumination} # Binary sensors can be created for handling front panel touch / release # events. To specify what part of the front panel to look at, the "part" # parameter can be set to: "any" (i.e. the default), "power button", # "color button" or "slider". binary_sensor: - # When touching the power button, toggle the light. + # When tapping the power button, toggle the light. # When holding the power button, turn on night light mode. - platform: xiaomi_bslamp2 id: ${id_power_button} @@ -155,7 +157,7 @@ binary_sensor: blue: 1 brightness: 0.01 - # When touching the color button, acivate the next preset. + # When tapping the color button, acivate the next preset. # When holding the color button, activate the next preset group. - platform: xiaomi_bslamp2 id: ${id_color_button} @@ -179,8 +181,8 @@ binary_sensor: sensor: # When the slider is touched, update the brightness. # Brightness 0.01 initiates the light night mode, which has already - # been handled above. Therefore, brightness starts from 0.02 here, - # so night mode is not triggered from the slider. + # been handled above (by holding the power button). Therefore, brightness + # starts from 0.02 here, to not trigger night mode using the slider. - platform: xiaomi_bslamp2 id: ${id_slider_level} range_from: 0.02 diff --git a/doc/flashing.md b/doc/flashing.md new file mode 100644 index 0000000..9222bbd --- /dev/null +++ b/doc/flashing.md @@ -0,0 +1,197 @@ +# Flashing guide + +## Tools needed + +* Allen key (2mm, 5/64") or torx (T8) screw driver +* Soldering Iron +* Perhaps some sticky tape +* A serial to USB adapter (FTDI) +* Some wires + +## Warning + +We have writen these instructions with care, but we will give absolutely no +warranty. Perhaps you will destroy your lamp and your computer. + +## Opening the lamp, to expose the PCB + +Remove the rubber pads from the botton of the lamp, to get access to 4 +screws that attach the bottom to the rest of the lamp. Note that you don't +have to remove these pads fully. Once you can access the screws, you've +gone far enough. + +Unbolt the 4 screws which were hidden under the rubber pads. + +![Photo of the screws](images/screws.jpg "Use an allen key or torx screw driver to remove the screws.") + +Detach the bottom from the rest of the lamp, exposing the PCB. This might +take a bit of force. Just pull it up bit by bit until it pops loose. + +For some good pictures of disassembling this lamp, take a look [at this +blog post](https://mysku.ru/blog/china-stores/78455.html) It is in Russian, +but the translation by Google works well and moreover, the pictures are the +most important thing here. If you scroll down, you will find them easily. + +## Solder the wires + +Many of the serial to USB adapter have some header pins to which you can +connect the wires of a device. Therefore, I find it most useful to take some +dupont wires with a female end to them, and cut off the other end. Strip the +wire on the other and, and solder this end to the board. + +Solder the wires to the `RX`, `TX`, `GND` and `GPIO0` debug pads that are +shown in the following photo. It is *not* required to solder a wire to the +`3.3V` debug pad. This pad is not directly connected to the 3.3V Vin of the +ESP32 chip, making it a less than optimal candidate for powering the board +during flashing. Instead, powering the lamp using its own power supply works +best. + +![Soldering points](images/Soldering_points.jpg) + +You can use some sticky tape to fixate the cables before soldering. + +## Connect the wires to your serial to USB adapter + +The wires must be connected as follows: + + | Soldering point| Serial USB Adapter name | + | -------------- |:------------------------:| + | GND | GND | + | TX | RX | + | RX | TX | + | GPIO0 | GND | + +To be able to flash the lamp, `GPIO0` must be connected to ground while +the lamp boots up. Therefore, connect these wires *before* plugging in +the lamp's power supply. Flashing will not work if you connect these +wires after the lamp has already been booted up. + +## When you only have one GND pin on your USB Adapter + +If your USB Adapter does not have multiple `GND` pins, then you'll have to +find another way to attach `GPIO0` to ground. Some options: + +- Use a breadbord, so you can connect the USB Adapter `GND` pin to a row on + the bread bord, and connect the `GND` and `GPIO0` wires of the lamp's + board to that same row. + +- Solder a button on the board that connects `GPIO0` to `GND` when pressed. + Then you can hold down this button while plugging in the lamp's power + supply. After booting up, you can release the button (the serial console + will also mention that flash mode is now enabled). This is not the most + practical solution for most people (since only one such flash operation is + needed, from then on OTA - Over The Air - updates are possible), but it + was a great help to me during the initial reverse engineering and firmware + development. + +- Manually hold a wire connected to both a GND surface (e.g. the silver pad + on the left of the board) and the `GPIO0` debug pad, while plugging in the + power supply. After booting, the wire can be removed. This is very fiddly + way of doing it (a third hand would be very welcome with this), but it can + be done. + +- You could opt for temporarily soldering a lead between `GND` and `GPIO0` + on the board, making `GPIO0` pulled to ground permanently. It is a bit + less flexible than some other options, but if you only need to do the + initial backup and firmware flash of the lamp, then this can be all + that you need. Remove the lead after flashing is done, otherwise the + lamp won't boot in normal mode. + +## Make a backup of the current firmware + +Backing up the firmware makes it possible to revert to the original firmware, +in case you have problems with the ESPHome firmware. The backup can be +created using "esptool". Installation instructures can be found here: + + https://github.com/espressif/esptool/blob/master/README.md#installation--dependencies + +Here's an example on how to backup the original firmware from Linux. First, +unplug your lamp's power supply, then start the esptool read_flash command: + +``` +python esptool.py -p /dev/ttyUSB0 read_flash 0x0 0x400000 original-firmware.bin +``` + +`/dev/ttyUSB0` is the port of the USB adaper on Linux. You can find what +port is used by the adapter by running `dmesg` after plugging in the USB +device. On Windows this is often `COM1`, `COM2` or `COM3`. + +Now plug in the power supply. The output of esptool should now show that it +connects to the lamp and downloads the firmware from it. + +**Caution**: You will find the WLAN SSID and Password of the last used +WiFi network in this file. Therefore, keep this backup in a safe place. + +## Restore the backed up firmware + +In case you need to rollback to the lamp's original firmware at some +point, here's an example of how to restore the original firmware from +Windows, by fully flashing it back onto the lamp. + +First, unplug your lamp's power supply, then start the esptool write_flash +command: + +``` +python.exe .\esptool.py --chip esp32 --port COM3 --baud 115200 write_flash 0x00 original-firmware.bin +``` + +Make sure that `GPIO0` is connected to GND and plug in the power supply. +The output of esptool should now show that it connects to the lamp and +uploads the firmware to it. Be patient after the upload reaches 100%. The +output is silent for a while, but esptool tool is verifying if the firmware +was uploaded correctly. + +After the firmware upload completes, unplug the power, disconnect `GPIO0` +from GND and reconnect the power supply to boot into the restored firmware. + +## Flash new ESPHome firmware + +Setup an ESPHome Project (see [README.md](../README.md)),compile the firmware +for the lamp and download the `firmware.bin` file to the device to which +the serial adapter is connected. + +You can flash the lamp using esphome or esptool. I would recommend using +the [esphome-flasher](https://github.com/esphome/esphome-flasher) tool, +which is a very easy to use GUI utility app for flashing ESPHome devices: + +- In the app, select the COM port of your serial adapter. +- Also select the firmware.bin file to flash onto the lamp. +- Power up the lamp with `GPIO0` connected to GND. +- Click the "Flash ESP" button to flash the firmware. + +If you want to flash with esptool, you can use: + +``` +python.exe .\esptool.py --chip esp32 --port COM3 --baud 115200 write_flash 0x1000 +``` + +After flashing, power down the lamp, disconnect `GPIO0` from GND and +reconnect the power to boot into the new ESPHome firmware. + +From here on, it is possible to flash the lamp OTA (over the air, which +means that the firmware is uploaded over WiFi) from ESPHome. Therefore, it +is now time to tuck away or remove those soldered wires and add the bottom +cover back on. + +## Troubleshooting flash + +If you have **A fatal error occurred: MD5 of file does not match data in +flash!**, then make sure you are powering the board using the lamp's own +power adapter. We've seen these errors when trying to power the board using +the 3.3V debug pad. + +After seeing this error, user @tabacha was able to successfully flash his +lamp using the regular power adapter and the tasmota boot loader using +the following command: + +``` +python esptool.py --chip esp32 -p /dev/ttyUSB0 --baud 115200 --before default_reset --after hard_reset write_flash -z --flash_mode dout --flash_freq 40m --flash_size detect 0x1000 bootloader_dout_40m.bin 0x8000 partitions.bin 0xe000 boot_app0.bin 0x10000 ~/Downloads/schlafzimmerbedlight.bin +``` + +You will find the missing tasmota boot files here: +https://github.com/arendst/Tasmota/tree/firmware/firmware/tasmota32/ESP32_needed_files + +*Note: user @tabacha was not able to use tasmota with the Bedside Lamp 2.* + +(remember that the [esphome-flasher](https://github.com/esphome/esphome-flasher) +will give you a bit less of a hard-core experience during flashing) diff --git a/doc/installation.md b/doc/installation.md new file mode 100644 index 0000000..57d725c --- /dev/null +++ b/doc/installation.md @@ -0,0 +1,59 @@ +# Installation guide + +The code must be compiled using ESPHome. Therefore, a prerequisite is that +you have ESPHome up and running in some form (command line, docker container, +web dashboard, possibly from within Home Assistant). +For information on this, please refer to the documentation on the +[ESPHome website](https://esphome.io). + +Create a folder named `custom_components` in the folder where your device's +yaml configuration file is stored. Then clone the the Github repo into a +subfolder `xiaomi_bslamp2`. For example on the command line: + +``` +# cd /your/path/to/config +# mkdir custom_components +# cd custom_components +# git clone https://github.com/mmakaay/esphome-xiaomi_bslamp2 xiaomi_bslamp2 +``` + +Your folder structure should now look like: +``` +config +├── yourdevice.yaml +├── custom_components/ +│ ├── xiaomi_bslamp2/ +│ . ├── README.md +. . ├── LICENSE.md +. . . +``` + +On a Rapsbery Pi with HomeAssistant and ESPHome as a plugin, the directory +should be: `/config/esphome/custom_components/xiaomi_bslamp2/` + +``` +config +├── esphome +│ ├── yourdevice.yaml +│ ├── custom_components/ +| . ├── xiaomi_bslamp2/ +│ . . ├── README.md +. . . ├── LICENSE.md +. . . . +``` + +Then create the required configuration in your device's yaml configuration +file. For an example file, take a look at the example file +[doc/example.yaml](doc/example.yaml) in this repository. +Detailed configuration instructions can be found in the +[Configuration guide](doc/configuration.md). + +After these steps you can compile your firmware `firmware.bin` file. +This firmware can then be flashed onto the device. + +Like normal with ESPHome, the first time you will have to flash the +device using a serial interface. After this initial flashing operation, you +can flash new versions of the firmware using the OTA (Over The Air) method. + +See [doc/flashing.md](the flashing guide) for hints on opening up the +device and flashing its firmware using its serial interface. diff --git a/doc/known_issues.md b/doc/known_issues.md new file mode 100644 index 0000000..21dbe31 --- /dev/null +++ b/doc/known_issues.md @@ -0,0 +1,83 @@ +# Known issues + +## The device keeps losing its connection to Home Assistant + +Disconnects are annoying, but even more annoying is that sometimes the +lamp will reboot (during which the light will be turned off) as a result of +these disconnects. The reasons for these reboots are very likely in the +underlying libraries, and I'm working on tracking these down. + +In the meanwhile, there are a few factors that you can look at to bring down +the number of disconnects: + +* A bug in the AsyncTCP library +* The number of connected API clients +* The logging output level + +**A bug in the AsyncTCP library** + +You might be running into a problem in the upstream library "AsyncTCP". +If you connect a serial console to your lamp and see "ack timeout 4" +messages in the logging output before the lamp disconnects the API client, +then you can be pretty sure that this is the culprit. + +I did identify the underlying issue and a pull request for a fix was +accepted and merged into the ESPHome fork of the library: + + https://github.com/OttoWinter/AsyncTCP/pull/4 + +This fix will likely be available in the next release of ESPHome +(the current version at the time of writing is 1.16.2). + +If you want to try out this fix on beforehand, then create a `libs` folder +in the folder where your device's yaml configuration file is stored (e.g. +when using the Home Assistant plugin: `/config/esphome/libs/`). +Then clone the following repository into that folder: + + https://github.com/OttoWinter/AsyncTCP + +For example on the command line: + +``` +# cd /config/esphome +# mkdir libs +# cd libs +# git clone https://github.com/OttoWinter/AsyncTCP +``` + +Then add a pointer to this folder from within your device's yaml +configuration file, using the `lib_extra_dirs` option. Provide it with the +absolute path to your `libs` folder. The relevant part of the config change +looks like this (matching the example from above): + +```yaml +esphome: + platformio_options: + lib_extra_dirs: /config/esphome/libs +``` + +This way, the repository version of the library will override the version of +the library that is bundled with ESPHome. Build the device firmware and +flash the device like you would normally do. + +**The number of connected API clients** + +Another factor on the connection stability, seems to be the number of +clients that are connected to the API. When connecting a log client via the +API (e.g. when looking at the logging from within the web dashboard), while +Home Assistant is also connected, disconnects might occur. + +When the lamp is in production, this is not an issue. Then only Home +Assistant is connected. + +**The logging output level** + +I have seen an increase in disconnects while the log level was set to +`VERY_VERBOSE`. The logging code might take up so much time, that it +interferes with the operation of the networking code. Especially since the +ESP32-WROOM-32D chip that is used in the lamp is a single core version of +the ESP32. + +For this reason, I advise to completely omit logging or use a very low log +level for production purposes. + diff --git a/doc/why_custom_firmware.md b/doc/why_custom_firmware.md new file mode 100644 index 0000000..b9175fb --- /dev/null +++ b/doc/why_custom_firmware.md @@ -0,0 +1,37 @@ +# Why custom ESPHome firmware? + +This lamp could always be added to Home Assistant using the Yeelight +integration. For this to work, the lamp had to be configured to enable a +special "LAN control" option, which provides the interface that the Yeelight +integration could talk to. + +January 2021, Xiaomi decided to remove the LAN control option from the +firmware. Information about this can be found on the Yeelight forums: + + https://forum.yeelight.com/t/topic/22664 + +In the forums, a work-around is offered: by providing your Mi ID, you can be +added to some mythical white list, through which you'll get a special +firmware upgrade that does have LAN control enabled. After that, no further +ugprades should be done. + +This just didn't feel right, so I started looking into alternative solutions +to regain control over the lamp. After opening it up, I was pleased to +find an ESP32-WROOM-32D chip inside, meaning that it might be possible to +build my own firmware for it using ESPHome. + +On the Home Assistant community forums, I found an existing thread where +people already started poking at the lamp. + + https://community.home-assistant.io/t/hacking-yeelight-fw-enabling-lan-control/284406 + +I joined the discussion and started poking as well. Some things turned out +to be more complicated than anticipated and quite a bit of reverse +engineering was required to make things work, but eventually this all +resulted in working firmware. + +Documents related to the reverse engineering process can be found in a +separate GitHub repository: + + https://github.com/mmakaay/esphome-yeelight_bs2-revengineering + diff --git a/front_panel_hal.h b/front_panel_hal.h index a191763..d435928 100644 --- a/front_panel_hal.h +++ b/front_panel_hal.h @@ -206,11 +206,12 @@ public: } /** - * Sets the front panel light to the provided level (0.0 - 1.0). + * Sets the front panel illumination to the provided level (0.0 - 1.0). * - * Level 0.0 means: turn off the front panel light. - * The other levels are translate to one of the avialable levels, - * represented by the backlight of the slider bar. + * Level 0.0 means: turn off the front panel illumination. + * The other levels are translated to one of the available levels, + * represented by the level indicator (i.e. the illumination of the + * slider bar.) */ void set_light_level(float level) { if (level == 0.0f) diff --git a/output/output.h b/output/output.h index eb1fc03..3ed852a 100644 --- a/output/output.h +++ b/output/output.h @@ -11,7 +11,7 @@ namespace bslamp2 { /** * An output, used for controlling the front panel illumination and - * brightness level indicator on the Xiaomi Mijia Bedside Lamp 2 front panel. + * level indicator on the Xiaomi Mijia Bedside Lamp 2 front panel. */ class XiaomiBslamp2FrontPanelLight : public output::FloatOutput, public Component { public: diff --git a/sensor/slider_sensor.h b/sensor/slider_sensor.h index f329c21..b80c6ff 100644 --- a/sensor/slider_sensor.h +++ b/sensor/slider_sensor.h @@ -14,10 +14,10 @@ namespace bslamp2 { * Xiaomi Mijia Bedside Lamp 2. * * This sensor publishes the level at which the slider was touched, so it - * can be used to implement automations. Note that it does not represent + * can be used to implement automations. Note that it does not represent * the brightness of the LED lights (this is implemented by the light output * component), nor the level as displayed by the slider using the front - * panel light (this is implemented by the slider light component). + * panel illumination (this is implemented by the slider output component). */ class XiaomiBslamp2SliderSensor : public sensor::Sensor, public Component { public: