Browse Source

Terminology consistency and preparing documentation for stable release.

pull/17/head
Maurice Makaay 3 years ago
parent
commit
702f24751a
12 changed files with 439 additions and 333 deletions
  1. +30
    -117
      README.md
  2. +1
    -1
      __init__.py
  3. +0
    -189
      doc/FLASHING.md
  4. +3
    -0
      doc/configuration.md
  5. +21
    -19
      doc/example.yaml
  6. +197
    -0
      doc/flashing.md
  7. +59
    -0
      doc/installation.md
  8. +83
    -0
      doc/known_issues.md
  9. +37
    -0
      doc/why_custom_firmware.md
  10. +5
    -4
      front_panel_hal.h
  11. +1
    -1
      output/output.h
  12. +2
    -2
      sensor/slider_sensor.h

+ 30
- 117
README.md View File

@ -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.

+ 1
- 1
__init__.py View File

@ -122,7 +122,7 @@ def make_front_panel_hal(config):
cg.add(fp_hal.set_i2c_address(config[CONF_ADDRESS])) cg.add(fp_hal.set_i2c_address(config[CONF_ADDRESS]))
def to_code(config): 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. # the code for the "gpio" platform for the "output" domain.
# Loading specific platform components is not possible using # Loading specific platform components is not possible using
# the AUTO_LOAD feature unfortunately. # the AUTO_LOAD feature unfortunately.


+ 0
- 189
doc/FLASHING.md View File

@ -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 <path\to\yourfirmware.bin>
```
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)

+ 3
- 0
doc/configuration.md View File

@ -0,0 +1,3 @@
# Configuration guide
TODO

+ 21
- 19
doc/example.yaml View File

@ -7,9 +7,9 @@ substitutions:
friendly_name: Bedside Lamp friendly_name: Bedside Lamp
transition_length: 500ms transition_length: 500ms
# Derive component identifiers, based on the device name.
# Derive component identifiers, based on the name.
id_light: ${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_power_button: ${name}_power_button
id_color_button: ${name}_color_button id_color_button: ${name}_color_button
id_slider_level: ${name}_slider_level id_slider_level: ${name}_slider_level
@ -36,11 +36,11 @@ api:
ota: ota:
password: "Password-For-Flashing-This-Device-Over-The-Air" 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: logger:
level: WARN level: WARN
@ -51,7 +51,7 @@ logger:
# Special platform + package are used for enabling unicore and disabling the # 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 # 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: esphome:
name: ${name} name: ${name}
platform: ESP32 platform: ESP32
@ -61,13 +61,13 @@ esphome:
platform_packages: |-4 platform_packages: |-4
framework-arduinoespressif32 @ https://github.com/pauln/arduino-esp32.git#solo-no-mac-crc/1.0.6 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: light:
# This component controls the LED lights of the device.
- platform: xiaomi_bslamp2 - platform: xiaomi_bslamp2
id: ${id_light} id: ${id_light}
name: ${friendly_name} RGBW Light name: ${friendly_name} RGBW Light
default_transition_length: ${transition_length} 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 # on the front panel accordingly. In night light mode, turn off
# the front panel illumination. # the front panel illumination.
on_brightness: on_brightness:
@ -78,11 +78,11 @@ light:
state: night state: night
then: then:
- output.set_level: - output.set_level:
id: ${id_front_panel_output}
id: ${id_front_panel_illumination}
level: 0 level: 0
else: else:
- output.set_level: - output.set_level:
id: ${id_front_panel_output}
id: ${id_front_panel_illumination}
level: !lambda return x; level: !lambda return x;
# You can use any effects that you like. These are just examples. # You can use any effects that you like. These are just examples.
effects: effects:
@ -117,25 +117,27 @@ light:
# This text sensor propagates the currently active light mode. # This text sensor propagates the currently active light mode.
# The possible light modes are: "off", "rgb", "white" and "night". # 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: text_sensor:
- platform: xiaomi_bslamp2 - platform: xiaomi_bslamp2
name: ${name} Light Mode name: ${name} Light Mode
id: ${id_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. # Value 0.0 turns off the illumination.
# Other values (up to 1.0) turn on the illumination and set the level # 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: output:
- platform: xiaomi_bslamp2 - platform: xiaomi_bslamp2
id: ${id_front_panel_output}
id: ${id_front_panel_illumination}
# Binary sensors can be created for handling front panel touch / release # 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" # 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", # parameter can be set to: "any" (i.e. the default), "power button",
# "color button" or "slider". # "color button" or "slider".
binary_sensor: 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. # When holding the power button, turn on night light mode.
- platform: xiaomi_bslamp2 - platform: xiaomi_bslamp2
id: ${id_power_button} id: ${id_power_button}
@ -155,7 +157,7 @@ binary_sensor:
blue: 1 blue: 1
brightness: 0.01 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. # When holding the color button, activate the next preset group.
- platform: xiaomi_bslamp2 - platform: xiaomi_bslamp2
id: ${id_color_button} id: ${id_color_button}
@ -179,8 +181,8 @@ binary_sensor:
sensor: sensor:
# When the slider is touched, update the brightness. # When the slider is touched, update the brightness.
# Brightness 0.01 initiates the light night mode, which has already # 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 - platform: xiaomi_bslamp2
id: ${id_slider_level} id: ${id_slider_level}
range_from: 0.02 range_from: 0.02


+ 197
- 0
doc/flashing.md View File

@ -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 <path\to\yourfirmware.bin>
```
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)

+ 59
- 0
doc/installation.md View File

@ -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.

+ 83
- 0
doc/known_issues.md View File

@ -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.

+ 37
- 0
doc/why_custom_firmware.md View File

@ -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

+ 5
- 4
front_panel_hal.h View File

@ -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) { void set_light_level(float level) {
if (level == 0.0f) if (level == 0.0f)


+ 1
- 1
output/output.h View File

@ -11,7 +11,7 @@ namespace bslamp2 {
/** /**
* An output, used for controlling the front panel illumination and * 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 { class XiaomiBslamp2FrontPanelLight : public output::FloatOutput, public Component {
public: public:


+ 2
- 2
sensor/slider_sensor.h View File

@ -14,10 +14,10 @@ namespace bslamp2 {
* Xiaomi Mijia Bedside Lamp 2. * Xiaomi Mijia Bedside Lamp 2.
* *
* This sensor publishes the level at which the slider was touched, so it * 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 * 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 * 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 { class XiaomiBslamp2SliderSensor : public sensor::Sensor, public Component {
public: public:


Loading…
Cancel
Save