@ -0,0 +1,460 @@ | |||
< [Installation guide](installation.md) | [Index](../README.md) | [Flashing guide](flashing.md) > | |||
# Configuration guide | |||
I think, the best starting point for creating your own yaml configuration, is to look at the | |||
[`example.yaml`](../example.yaml) file and the [configuration packages](../packages). | |||
These configuration files were written with the functionality of the original firmware in | |||
mind and it makes use of all available options. This configuration guide can be used to fill | |||
in the blanks. | |||
The `xiaomi_bslamp2` platform provides various components that expose the core functionalities of | |||
the lamp. In the following table, you can find what components are used for exposing what physical | |||
components of the lamp. | |||
| Part | Component(s) | | |||
| -------------------------- |------------------------------------------------------------------| | |||
| ESP32 pinouts | [platform xiaomi_bslamp2](#platform-xiaomi_bslamp2) | | |||
| RGBWW LEDs | [light](#light) | | |||
| Front Panel Power button | [binary_sensor](#component-binary_sensor) | | |||
| Front Panel Color button | [binary_sensor](#component-binary_sensor) | | |||
| Front Panel Slider | [binary_sensor](#component-binary_sensor) (touch/release) | | |||
| | [sensor](#component-sensor) (touched slider level) | | |||
| Front Panel Illumination | [output](#component-output) (on/off + indicator level) | | |||
| Light mode propagation | [text_sensor](#component-text_sensor) | | |||
## Platform: xiaomi_bslamp2 | |||
At the core of the hardware support is the `xiaomi_bslamp2` platform, which provides two hub-style | |||
hardware abstraction layer (HAL) components that are used by the other components: one for driving | |||
the GPIO's for the RGBWW leds and one for the I2C communication between the ESP32 and the front | |||
panel. | |||
You will not have to add any configuration options for the `xiaomi_bslamp2` to your yaml file. | |||
The required configuration is fully provided by the configuration package `packages/core.yaml`. | |||
The GPIO + I2C configurations are prepared to work for the Bedside Lamp 2 wiring out of the box. | |||
```yaml | |||
xiaomi_bslamp2: | |||
``` | |||
The only reason that I can think of for adding this platform configuration to your yaml file, would | |||
be if you blew one or more or the ESP32 pins, and need to rewire functionality. In other cases, | |||
simply omit the section. | |||
## Component: light | |||
The light component creates an RGBWW light. This means that it can do colored light and cold/warm | |||
white light based on a color temperature. | |||
```yaml | |||
light: | |||
- platform: xiaomi_bslamp2 | |||
name: My Bedside Lamp | |||
id: my_bedside_lamp | |||
default_transition_length: 0.5s | |||
effects: | |||
- random: | |||
name: Randomize | |||
transition_length: 3s | |||
update_interval: 3s | |||
on_brightness: | |||
- then: | |||
- logger.log: The brightness changed! | |||
presets: | |||
my_color_presets: | |||
red: { red: 100%, green: 0%, blue: 0% } | |||
green: { red: 0%, green: 100%, blue: 0% } | |||
blue: { red: 0%, green: 0%, blue: 100% } | |||
yellow: { red: 100%, green: 100%, blue: 0% } | |||
purple: { red: 100%, green: 0%, blue: 100% } | |||
randomize: { effect: Randomize } | |||
my_white_presets: | |||
cold: { color_temperature: 153 mireds } | |||
chilly: { color_temperature: 275 mireds } | |||
luke: { color_temperature: 400 mireds } | |||
warm: { color_temperature: 588 mireds | |||
``` | |||
### Configuration variables: | |||
* **name** (**Required**, string): The name of the light. | |||
* **id** (*Optional*, ID): Manually specify the ID used for code generation. By providing an id, | |||
you can reference the light from automation rules (e.g. to turn on the light when the power button | |||
is tapped) | |||
* **default_transition_length** (*Optional*, Time): The transition length to use when no transition | |||
length is set in a light call. Defaults to 1s. | |||
* **effects** (*Optional*, list): A list of | |||
[light effects](https://esphome.io/components/light/index.html#light-effects) to use for this light. | |||
* **presets** (*Optional*, dict): Used to define presets, that can be used from automations. See | |||
[below](#light-presets) for detailed information. | |||
* **on_brightness** (*Optional*, Action): An automation to perform when the brightness of the light | |||
is modified. | |||
* All other options from | |||
[the base Light implementation](https://esphome.io/components/light/index.html#config-light), | |||
except for options that handle color correction options like `gamma_correct` and `color_correct`. | |||
These options are superceded by the fact that the light component has a fully customized light | |||
model, that closely follows the light model of the original lamp's firmware. | |||
### Light modes | |||
The lamp supports multiple light modes. These are: | |||
* **RGB light** (input: RGB + brightness > 1%) | |||
* **White light** (input: Color Temperature + brightness > 1%) | |||
* **Night light** (input: RGB or White light + brightness at 1%) | |||
In the original firmware + Yeelight Home Assistant integration, the night light feature is | |||
implemented through a switch component. The switch can be turned on to activate the night light | |||
mode. In this ESPHome firmware, setting the brightness to its lowest value triggers the night light | |||
mode. This makes things a lot easier to control. | |||
It is possible to control the night light mode separately. An example of this can be found in the | |||
[example.yaml](../example.yaml), in which holding the power button is bound to activating the night | |||
light. | |||
### `light.disco_on` Action | |||
This action sets the state of the light immediately (i.e. without waiting for the next main loop | |||
iteration), without saving the state to memory and without publishing the state change. | |||
```yaml | |||
on_...: | |||
then: | |||
- light.disco_on: | |||
id: my_bedside_lamp | |||
brightness: 80% | |||
red: 70% | |||
green: 0% | |||
blue: 100% | |||
``` | |||
The possible configuration options for this Action are the same as those for the standard | |||
`light.turn_on` Action. | |||
### `light.disco_off` Action | |||
This action turns off the disco mode by restoring the state of the lamp to the last known state from | |||
before using the disco mode. | |||
```yaml | |||
on_...: | |||
then: | |||
- light.disco_off: | |||
id: my_bedside_lamp | |||
``` | |||
### Light presets | |||
The presets functionality was written with the original lamp firemware functionality in mind: the | |||
user has two groups of presets available: one for RGB light presets and one for white light presets | |||
(based on color temperature). The color button (the top one on the front panel) can be tapped to | |||
switch to the next preset within the active preset group. The same button can be held for a little | |||
while, to switch to the other preset group. | |||
In your light configuration, you can mimic this behavior (in fact: it is done so in the | |||
[example.yaml](../example.yaml)) by means of the presets system. This system consists of two parts: | |||
* Defining presets | |||
* Activating presets from automations | |||
**Defining presets** | |||
Presets can be configured in the `presets` option of the `light` configuration. | |||
Presets are arranged in groups. You can define as little or as many groups as you like. The example | |||
configuration uses two groups, but that is only to mimic the original behavior. If you only need one | |||
group, then create one group. If you need ten, go ahead and knock yourself out. | |||
The general structure of the presets configuration is: | |||
```yaml | |||
light: | |||
presets: | |||
group_1: | |||
preset_1: ... | |||
preset_2: ... | |||
.. | |||
group_2: | |||
preset_1: ... | |||
preset_2: ... | |||
.. | |||
.. | |||
``` | |||
*Note: Duplicate template names are ok, as long as they are within their own group. If you use | |||
duplicate preset names within a single group, then the last preset will override the earlier | |||
one(s).* | |||
A preset can define one of the following: | |||
* **RGB light** | |||
* **red** (**Optional**, percentage): the red component of the RGB value (default = 0%). | |||
* **green** (**Optional**, percentage): the green component of the RGB value (default = 0%). | |||
* **blue** (**Optional**, percentage): the blue component of the RGB value (default = 0%). | |||
* **brightness** (*Optional*, percentage): the brightness to use (default = current brightness). | |||
* **transition_length** (*Optional*, time): the transition length to use. | |||
* **White light** | |||
* **color_temperature** (**Required**, mireds): the color temperature in mireds (range: "153 mireds" - "588 mireds") | |||
* **brightness** (*Optional*, percentage): the brightness to use (default = current brightness). | |||
* **transition_length** (*Optional*, time): the transition length to use. | |||
* **Light effect** | |||
* **effect** (**Required**, string): the name of a light effect to activate. | |||
* **Brightness change only** | |||
* **brightness** (**Required**, percentage): the brightness to use. | |||
**Activating presets from automations** | |||
Once presets have been configured, they can be activated using the `preset.activate` action. The | |||
following options are available for this action: | |||
* Switch to next preset group (and after the last, switch to the first): | |||
```yaml | |||
preset.activate: | |||
next: group | |||
``` | |||
* Switch to next preset within currentl preset group (and after the last, | |||
switch to the first): | |||
```yaml | |||
preset.activate: | |||
next: preset | |||
--- | |||
* Activate a specific preset group by specifying the group's name: | |||
```yaml | |||
preset.activate: | |||
group: rgb | |||
``` | |||
* Activate a specific preset by specifying both the preset's name and group name: | |||
```yaml | |||
preset.activate: | |||
group: white | |||
preset: warm | |||
``` | |||
Shorthand definitions are available for all these actions: | |||
```yaml | |||
preset.activate: next_group | |||
preset.activate: next_preset | |||
preset.activate: rgb | |||
preset.activate: white.warm | |||
``` | |||
**Handling of invalid input** | |||
When a group or template is specified that does not exist, or if next group/preset is used while no | |||
presets have been defined at all, then the action will be ignored and an error will be logged. | |||
## Component: binary_sensor | |||
Binary sensors can be added to the configuration for handling touch/release events for the front | |||
panel. On touch, a binary_sensor will publish `True`, on release it will publish `False`. The | |||
configuration of a binary_sensor determines what part of the front panel is involved in the touch | |||
events. | |||
```yaml | |||
binary_sensor: | |||
- platform: xiaomi_bslamp2 | |||
id: my_bedside_lamp_power_button | |||
for: POWER_BUTTON | |||
on_press: | |||
then: | |||
- light.toggle: my_bedside_lamp | |||
``` | |||
For referencing the parts of the front panel, the following part identifiers are available: | |||
* POWER_BUTTON (or its alias: POWER) | |||
* COLOR_BUTTON (or its alias: COLOR) | |||
* SLIDER | |||
If personal taste dictates so, you can use lower case characters and spaces instead of underscores. | |||
This means that for example "Power Button" and "power" would be valid identifiers for the power | |||
button. | |||
### Configuration variables: | |||
* **name** (*Optional*, string): The name of the binary sensor. Setting a name will expose the | |||
binary sensor as an entity in Home Assistant. If you do not need this, you can omit the name. | |||
* **id** (*Optional*, ID): Manually specify the ID used for code generation. By providing an id, | |||
you can reference the binary_sensor from automation rules (to retrieve the current state of the | |||
binary_sensor). | |||
* **for** (*Mandatory*, part identifier): This specifies to for part of the front panel the binary | |||
sensor must report touch events. | |||
* All other options from | |||
[Binary Sensor](https://esphome.io/components/binary_sensor/index.html#config-binary-sensor). | |||
## Component: sensor | |||
The sensor component publishes touch events for the front panel slider. The published value | |||
represents the level at which the slider was touched. | |||
*Note: This sensor only reports the touched slider level. It cannot be used for detecting release | |||
events. If you want to handle touch/release events for the slider, then you can make use of the | |||
[binary_sensor](#component-binary_sensor) instead.* | |||
```yaml | |||
sensor: | |||
- platform: xiaomi_bslamp2 | |||
- id: my_bedside_lamp_slider_level | |||
range_from: 0.2 | |||
range_to: 0.9 | |||
on_value: | |||
then: | |||
- light.turn_on: | |||
id: my_bedside_lamp | |||
brightness: !lambda return x; | |||
``` | |||
### Configuration variables: | |||
* **name** (*Optional*, string): The name of the sensor. Setting a name will expose the sensor as an | |||
entity in Home Assistant. If you do not need this, you can omit the name. | |||
* **id** (*Optional*, ID): Manually specify the ID used for code generation. By providing an id, | |||
you can reference the sensor from automation rules (e.g. to retrieve the current state of the | |||
binary_sensor). | |||
* **range_from** (*Optional*, float): By default, published values vary from the range 0.01 to 1.00, | |||
in 20 steps. This option modifies the lower bound of the range. | |||
* **range_to** (*Optional*, float): This option modifies the upper bound of the range. | |||
* All other options from | |||
[Sensor](https://esphome.io/components/sensor/index.html#config-sensor). | |||
## Component: output | |||
The (float) output component is linked to the front panel illumination + level indicator. Setting | |||
this output (using the standard `output.set_level` action) to value 0.0 will turn off the frontpanel | |||
illumination. Other values, up to 1.0, will turn on the illumination and will set the level indicator | |||
to the requested level (in 10 steps). | |||
```yaml | |||
output: | |||
- platform: xiaomi_bslamp2 | |||
id: my_bedside_lamp_front_panel_illumination | |||
``` | |||
### Configuration variables: | |||
* **id** (**Required**, ID): The id to use for this output component. | |||
* All other options from [Output](https://esphome.io/components/output/index.html) | |||
### Addressing the LEDs of the illumination individually | |||
While the standard `output.set_level` action emulates the front panel illumination behavior | |||
of the original device firmware, it is also possible to control all of the LEDs for this | |||
illumination individually, in case you need some different behavior, e.g. leaving the | |||
power button on at night, so the user can easily find it in the dark. | |||
To address the LEDs, the following identifiers can be used in your YAML configuration: | |||
* `POWER` : The power button illumination. | |||
* `COLOR` : The color button illumination. | |||
* `1`, `2`, .., `10` : The 10 LEDs on the slider, where LED `1` is closest to the | |||
power button and LED `10` is closest to the color button. | |||
* `ALL` : represents all of the available LEDs | |||
* `NONE` : represents none of the available LEDs | |||
#### `front_panel.set_leds` Action | |||
This action turns on the provided LEDs, all other LEDs are turned off. | |||
```yaml | |||
on_...: | |||
then: | |||
- front_panel.set_leds: | |||
leds: | |||
- POWER | |||
- COLOR | |||
- 1 | |||
- 2 | |||
- 3 | |||
``` | |||
The `leds:` key can also be omitted here, making the following action calls | |||
equivalent to the one above. | |||
```yaml | |||
on_...: | |||
then: | |||
- front_panel.set_leds: | |||
- POWER | |||
- COLOR | |||
- 1 | |||
- 2 | |||
- 3 | |||
``` | |||
This can also be written as: | |||
```yaml | |||
on_...: | |||
then: | |||
- front_panel.set_leds: [ POWER, COLOR, 1, 2, 3 ] | |||
``` | |||
If only one LED is specified, you are allowed to omit the list definition: | |||
```yaml | |||
on_...: | |||
then: | |||
- front_panel.set_leds: POWER | |||
``` | |||
#### `front_panel.turn_on_leds` Action | |||
This action turns on the provided LEDs, and leaves the rest of the LEDs as-is. | |||
The LEDs to affect are specified in the same wat as above for `front_panel.set_leds`. | |||
#### `front_panel.turn_off_leds` Action | |||
This action turns off the provided LEDs, and leaves the rest of the LEDs as-is. | |||
The LEDs to affect are specified in the same wat as above for `front_panel.set_leds`. | |||
#### `front_panel.set_level` Action | |||
This action works like the `output.set_level` action, but it only updates the | |||
LEDs of the slider. The LEDs for the power and color button are left as-is. | |||
```yaml | |||
on_...: | |||
then: | |||
- front_panel.set_level: 0.5 | |||
``` | |||
#### `front_panel.update_leds` Action | |||
The previous actions only modify the required state for the front panel LEDs. | |||
Updating the actual state of the LEDs is done when the main loop for the | |||
output component is run by ESPHome. | |||
If you need the required state to be pushed to the LEDs immediately, regardless | |||
the main loop, then this action can ben used to take care of this. | |||
*Note: In most situations, you will not need to use this action explicitly | |||
to make the LEDs update. Only use it when you are sure that this is required.* | |||
```yaml | |||
on_...: | |||
then: | |||
- front_panel.set_leds: POWER | |||
- front_panel.update_leds: | |||
``` | |||
## Component: text_sensor | |||
The text sensor component publishes changes in the active [light mode](#light-modes). Possible | |||
output values for this sensor are: "off", "rgb", "white" and "night". | |||
### Configuration variables: | |||
* **name** (*Optional*, string): The name of the text sensor. Setting a name will expose the text | |||
sensor as an entity in Home Assistant. If you do not need this, you can omit the name. | |||
* **id** (*Optional*, ID): Manually specify the ID used for code generation. By providing an id, | |||
you can reference the text sensor from automation rules (to retrieve the current state of the | |||
text_sensor). | |||
* All other options from | |||
[Text Sensor](https://esphome.io/components/text_sensor/index.html) | |||
< [Installation guide](installation.md) | [Index](../README.md) | [Flashing guide](flashing.md) > |
@ -0,0 +1,287 @@ | |||
< [Configuration guide](configuration.md) | [Index](../README.md) | [Technical details](technical_details.md) > | |||
# Flashing guide | |||
Table of contents: | |||
* [Warning](#warning) | |||
* [Tools needed](#tools-needed) | |||
* [Opening the lamp, to expose the PCB](#opening-the-lamp-to-expose-the-pcb) | |||
* [Solder wires to the board](#solder-wires-to-the-board) | |||
* [Connect the wires to your serial to USB adapter](#connect-the-wires-to-your-serial-to-usb-adapter) | |||
* [When you only have one GND pin on your USB Adapter](#when-you-only-have-one-gnd-pin-on-your-usb-adapter) | |||
* [Make a backup of the current firmware](#make-a-backup-of-the-current-firmware) | |||
* [How to restore the backed up firmware](#how-to-restore-the-backed-up-firmware) | |||
* [Flash new ESPHome firmware](#flash-new-esphome-firmware) | |||
* [Troubleshooting flash](#troubleshooting-flash) | |||
## Warning | |||
We have writen these instructions with care, but we will give absolutely no warranty. Perhaps you | |||
will destroy your lamp and your computer. | |||
## Tools needed | |||
* Allen key (2mm, 5/64") or torx (T8) screw driver | |||
* Soldering Iron | |||
* A serial to USB adapter (for example FTDI) that can provide 3.3V RX/TX signals | |||
* Some wires | |||
* Optional: sticky tape, hot glue gun, magnifying glass | |||
## Opening the lamp, to expose the PCB | |||
*Tip: you can click on all images from below to view them in full size.* | |||
Remove the rubber pads from the bottom of the lamp, to get access to 4 screws that attach the bottom | |||
to the rest of the lamp. | |||
<img src="../images/01_unboxed.jpg" width="200"><img src="../images/02_remove_rubber_pads.jpg" width="200"> | |||
Note that you don't have to remove these pads fully. Once you can access the screws, you've gone far | |||
enough. | |||
<img src="../images/03_bolts_overview.jpg" width="200"> | |||
Unbolt the 4 screws which were hidden under the rubber pads. | |||
<img src="../images/04_remove_bolts.jpg" width="200"><img src="../images/05_bolts.jpg" width="200"> | |||
Detach the bottom from the rest of the lamp, exposing the PCB. This might take a bit of force. Just | |||
pull it off bit by bit, until it pops loose. | |||
<img src="../images/06_pull_off_the_bottom.jpg" width="200"><img src="../images/07_bottom_removed.jpg" width="200"> | |||
<img src="../images/08_board_exposed.jpg" width="400"> | |||
## Solder wires to the board | |||
The wires will be connected to the debug pads that are shown in the following image. | |||
<img src="../images/09_debug_pads_for_soldering.jpg" width="400"> | |||
Many of the serial to USB adapter have some header pins to which you can connect the wires of a | |||
device (no soldering required). Therefore, it might be useful to use dupont wire. Cut off one end, | |||
strip the wire, tin the wire and solder it to the board. | |||
*Note: Whether to use male or female dupont wires depends on how you want to connect the serial | |||
adapter. In this example, I have used male wires, so I could plug them into a breadbord.* | |||
<img src="../images/10_stripped_dupont_wires.jpg" width="200"> | |||
Solder the wires to the `RX`, `TX`, `GND` and `GPIO0` debug pads. | |||
Beware not to use too much solder on the GPIO0 pad, because that might flow onto the pad that is right | |||
next to it, permanently putting the device in flash mode as a result. | |||
*Note: The board has a debug pad that exposes 3.3V. Do not use this pad to power the board from your | |||
serial adapter. Always power the lamp using its own power supply.* | |||
A few tips: | |||
- Depending on the quality of your eyes, you might want to use a magnifying glass for the soldering | |||
work. Use one that is mounted on a stand, or you will quickly be left wishing that you could grow | |||
a third arm. | |||
- You could use some sticky tape to fixate the cables before soldering. | |||
- When you want to keep the wires attached after flashing the new firmware (e.g. for serial logging | |||
or for future firmware flashing), then you might want to apply some hot glue to fixate the wires. | |||
This prevents the wires from breaking off, due to excessive movement. | |||
<img src="../images/11_soldered_wires.jpg" width="200"><img src="../images/12_optional_hot_glue.jpg" width="200"> | |||
## Connect the wires to your serial to USB adapter | |||
Make sure that your adapter uses 3.3V for the RX/TX pins that you will connect to the lamp. Some of | |||
these adapters allow you to switch between 3.3V and 5V using a switch or a jumper. Do not use an | |||
adapter that only provides 5V output. Reason for this, is that the ESP32 chip works at 3.3V. I have | |||
seen the chips accept 5V serial input (I did flash the lamp successfully like that), but I am not | |||
sure if they are actually 5V tolerant. Better safe than sorry in such case! | |||
The wires must be connected as follows: | |||
| Soldering point| Serial USB Adapter name | | |||
| -------------- |:------------------------:| | |||
| GND | GND | | |||
| TX | RX | | |||
| RX | TX (3.3V) | | |||
| 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. The rest of this guide | |||
will show this method. | |||
[View example by @mmakaay](../images/13_connect_to_serial_to_usb_adapter.jpg). | |||
- **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. Some example implementations: | |||
[a crude one by @mmakaay](../images/13_connect_to_serial_with_button.jpg), | |||
[one by @edwinschoonhoven](../images/13_connect_to_serial_with_button_alternative.jpg) and | |||
[one by @mmakaay, inspired by Erwin's](../images/13_connect_to_serial_with_button_alternative2.jpg). | |||
- **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. | |||
- **Temporarily solder 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. | |||
[View example by @erwinschoonhoven](../images/13_connect_to_serial_with_soldered_gnd.jpg). | |||
In the following images, you will see the first solution, using a breadboard. | |||
<img src="../images/13_connect_to_serial_to_usb_adapter.jpg" width="400"> | |||
In close up: | |||
<img src="../images/14_connect_to_serial_to_usb_adapter_close_up.jpg" width="400"> | |||
You can now connect the serial to USB adapter to you computer. Pay special attention to the | |||
cross-over of the TX/RX pair (TX connects to RX and vice versa). Start the | |||
[esphome-flasher tool](https://github.com/esphome/esphome-flasher) and select the COM port to use. | |||
Then click on "View logs". | |||
Now, plug in the lamp's power supply to boot up the lamp. | |||
<img src="../images/15_power_up_for_flashing.jpg" width="400"> | |||
Because GPIO0 is connected to GND, the device should start up in flashing mode. If all went well, | |||
the log output in esphome-flasher looks somewhat like this: | |||
<img src="../images/16_serial_showing_download_mode.png" width="400"> | |||
## 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 back 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. | |||
## How to 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 while esptool tool is verifying that | |||
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 strongly recommend using the | |||
[esphome-flasher](https://github.com/esphome/esphome-flasher) tool. This is a very easy to use GUI | |||
utility app for flashing ESPHome devices and for viewing serial console logging. | |||
- In the app, select the COM port of your serial adapter. | |||
- Then 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 all went well, the final log output in esphome-flasher looks somewhat like this: | |||
<img src="../images/17_flash_ready.png" width="400"> | |||
If you want to flash with esptool, you can use the following command. | |||
*Note: unless you know exactly what you're doing with esptool here, I recommend to use the | |||
esphome-flasher instead.* | |||
``` | |||
python esptool.py --chip esp32 -p /dev/ttyUSB0 --baud 115200 \ | |||
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 firmware.bin | |||
``` | |||
The required .bin files can be found in the following locations: | |||
- **bootloader_dout_40m.bin**: [from arduino-esp32 package](https://github.com/mmakaay/arduino-esp32-unicore-no-mac-crc/blob/v1.0.6/tools/sdk/bin/bootloader_dout_40m.bin) in `tools/sdk/bin/` | |||
- **partitions.bin**: from `<config dir>/<device name>/.pioenvs/<device name>/partitions.bin` | |||
- **boot_app0.bin**: [from arduino-esp32 package](https://github.com/mmakaay/arduino-esp32-unicore-no-mac-crc/blob/v1.0.6/tools/partitions/boot_app0.bin) in `tools/partitions/` | |||
- **firmware.bin**: from `<config dir>/<device name>/.pioenvs/<device name>/firmware.bin` | |||
After flashing, power down the lamp, disconnect `GPIO0` from GND and reconnect the power to boot | |||
into the new ESPHome firmware. | |||
<img src="../images/18_disconnect_GPIO0.jpg" width="200"> | |||
The lamp should now be operational using the new firmware. | |||
<img src="../images/19_test_run.jpg" width="200"> | |||
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. | |||
Because I want to keep them around for future use, I tuck them away, making sure that the connectors | |||
don't touch each other or the board. | |||
<img src="../images/20_tuck_away_wires.jpg" width="200"> | |||
The bottom cover can now be put back on. The lamp is ready for use. | |||
<img src="../images/21_reassemble_and_enjoy.jpg" width="200"> | |||
## 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. | |||
< [Configuration guide](configuration.md) | [Index](../README.md) | [Technical details](technical_details.md) > |
@ -0,0 +1,38 @@ | |||
< [Why custom firmware?](why_custom_firmware.md) | [Index](../README.md) | [Configuration guide](configuration.md) > | |||
# Installation guide | |||
The code must be compiled into a firmware using ESPHome 2021.10.0 or later. 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 as an add-on). For information on this, please refer to the | |||
documentation on the [ESPHome website](https://esphome.io). | |||
The component code is distributed directly from GitHub. You will not have to download and install | |||
the code manually. This leverages the [external components](https://esphome.io/components/external_components.html) | |||
feature. | |||
Before you can compile the firmware, you will have to create the YAML configuration file for your | |||
device. You can take the [`example.yaml`](../example.yaml) from this repository as a starting point, and | |||
modify that one to your needs. Detailed information about the YAML configuration options can be | |||
found in the [Configuration guide](configuration.md). | |||
After these steps you can let ESPHome compile your firmware (`firmware.bin`) file. This firmware | |||
can then be flashed onto the device. | |||
**Note**: During compilation you might see some warning messages from ESPHome, telling you: | |||
``` | |||
WARNING GPIO4 is a Strapping PIN and should be avoided. | |||
... | |||
WARNING GPIO2 is a Strapping PIN and should be avoided. | |||
``` | |||
You can safely ignore these type of warnings. GPIO4 and GPIO2 are hard-wired in the lamp's PCB. | |||
The way in which the firmware uses these pins does not pose any problems for 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 [the flashing guide](flashing.md) for hints on opening up the device and flashing its firmware | |||
using the serial interface. | |||
< [Why custom firmware?](why_custom_firmware.md) | [Index](../README.md) | [Configuration guide](configuration.md) > |
@ -0,0 +1,37 @@ | |||
# Release plan | |||
All development is done in the `dev` branch. Once the development branch is | |||
ready for a new production release, the following steps are followed: | |||
**Update the CHANGELOG.md** | |||
Make sure all notable changes are recorded in the changelog. | |||
**Update ESPHome version information** | |||
The minimum ESPHome version requirement must be documented correctly in the following files: | |||
* doc/installation.md (at the start of the file) | |||
* README.md (at the start of the quick start guide) | |||
* CHANGELOG.md (at the start of the log for the released version) | |||
**Create version release branch** | |||
Branch `dev` to a `release/<version>` branch, e.g. `release/2021.10.0`. | |||
``` | |||
$ git checkout dev | |||
$ git checkout -b release/2021.10.0 | |||
``` | |||
**Update project version information** | |||
The version of this project (e.g. `2021.10.0`) must be updated in: | |||
* `example.yaml` (the `ref:` for the bslamp2 configuration package) | |||
* `packages/core.yaml` (the `ref:` for the external component) | |||
**Push the release to GitHub** | |||
``` | |||
$ git push --set-upstream origin release/2021.10.0 | |||
``` |
@ -0,0 +1,20 @@ | |||
< [Technical details](technical_details.md) | [Index](../README.md) | |||
# Sponsoring the project | |||
All development on this firmware is done for the greater good of mankind. You can use it for free. | |||
However, if you do feel the uncontrollable urge to contribute financially to the project, you can do | |||
so through [this PayPal link](https://www.paypal.com/paypalme/bedsidelamp2). | |||
This account is used for acquiring hardware and software licenses that I use for my open source | |||
development. Specific hardware that I bought for this project were: | |||
* An extra lamp for development and testing, so I don't annoy my wife with buggy deployements ;-) | |||
* A dedicated serial to USB adapter, connected to the development lamp. | |||
* a simple 8 port logic analyzer that helped me with reverse engineering the I2C protocol of the | |||
front panel. | |||
* Various wires and buttons. | |||
* An Arduino Uno R3, to [help out somebody in the community forums with | |||
flashing the lamp using the Arduino](https://community.home-assistant.io/t/hacking-yeelight-fw-enabling-lan-control/284406/214?u=mmakaay). | |||
< [Technical details](technical_details.md) | [Index](../README.md) |
@ -0,0 +1,381 @@ | |||
< [Flashing guide](flashing.md) | [Index](../README.md) | [Sponsoring](sponsoring.md) > | |||
# Technical details | |||
In this section, you can find some of the information that was gathered during the reverse | |||
engineering of the Bedside Lamp 2 hardware. | |||
Table of contents: | |||
* [High level overview](#high-level-overview) | |||
* [ESP32 pinout](#esp32-pinout) | |||
* [Front panel](#front-panel) | |||
* [Build requirements](#build-requirements) | |||
* [Original firmware](#original-firmware) | |||
## High level overview | |||
No documentation is complete without some ASCII art schematics. | |||
``` | |||
RX/TX/GND for | |||
12V power supply flashing and logs | |||
| | | |||
v | Front panel | |||
+---------------+ +---------------+ .---. | |||
| Power supply |---- 3.3V -.----->| ESP-WROOM-32D |<- I2C ->| O | -- color | |||
+---------------+ \ | single core | | | button | |||
| \ | 4 MB flash |<- IRQ --| | | | |||
12V \ +---------------+ | | | | |||
| `-------|------------------->| | | -- slider | |||
v | | | | | |||
+---------------+ | | | | | |||
| RGB and white |<---- RGBW + master ---+ | | power | |||
| LED circuitry | PWM on/off | O | -- button | |||
+---------------+ `---` | |||
``` | |||
The LED circuitry provides two light modes: | |||
* Colored RGB light; | |||
* Warm to cool white light. | |||
The front panel of the device contains a two touch buttons (power on/off and color selection) and a | |||
touch slider (for setting the brightness level). This panel is lit when the device is turned on. The | |||
light behind the slider will represent the actual brightness setting of the device. | |||
## ESP32 pinout | |||
In the following image, you can find the pinout as used for the ESP32: | |||
<img src="../images/hardware/ESP32_pinout.jpg" width="600"> | |||
Here's an overview of all exposed pins of the chip, starting at the GND + 3.3V pins, and going | |||
anti-clockwise. The table shows not only the functions of the pins that are actually in use by the | |||
lamp's circuitry, but also the pins that are not in use and their possible use. | |||
| PIN | GPIO# | Function | Description | Possible use | | |||
|------|--------|-----------|--------------------------------|--------------| | |||
| GND | | Ground | Connected to ground | - | | |||
| 3.3V | | Power | Power supply input | - | | |||
| 9 | | Reset | Can be pulled to GND to reset | - | | |||
| 5 | GPIO36 | - | | IN | | |||
| 8 | GPIO39 | - | | IN | | |||
| 10 | GPIO34 | - | | IN | | |||
| 11 | GPIO35 | - | | IN | | |||
| 12 | GPIO32 | - | | IN/OUT | | |||
| 13 | GPIO33 | LEDs | LEDs, master switch 1 | - | | |||
| 14 | GPIO25 | ??? | 10k pull up, unknown function | IN/OUT (1) | | |||
| 15 | GPIO26 | - | | IN/OUT | | |||
| 16 | GPIO27 | - | | IN/OUT | | |||
| 17 | GPIO14 | LEDs | LEDs, green PWM channel | - | | |||
| 18 | GPIO12 | LEDs | LEDs, white PWM channel | - | | |||
| GND | | Ground | Connected to ground | - | | |||
| 20 | GPIO13 | LEDs | LEDs, red PWM channel | - | | |||
| 28 | GPIO9 | SPI | SPI flash memory | - | | |||
| 29 | GPIO10 | SPI | SPI flash memory | - | | |||
| 30 | GPIO11 | SPI | SPI flash memory | - | | |||
| 31 | GPIO6 | SPI | SPI flash memory | - | | |||
| 32 | GPIO7 | SPI | SPI flash memory | - | | |||
| 33 | GPIO8 | SPI | SPI flash memory | - | | |||
| 21 | GPIO15 | - | | IN/OUT (2) | | |||
| 22 | GPIO2 | - | Debug pad, no function | IN/OUT (3) | | |||
| 23 | GPIO0 | Boot mode | Pull to GND for flashing mode | - | | |||
| 24 | GPIO4 | LEDs | LEDs, master switch 2 | - | | |||
| 25 | GPIO16 | Front pnl | Front panel interrupt | - | | |||
| 27 | GPIO17 | EEPROM | EEPROM I2C SDA (4) | - | | |||
| 34 | GPIO5 | LEDs | LEDs, blue PWM channel | - | | |||
| 35 | GPIO18 | EEPROM | EEPROM I2C CLK (4) | - | | |||
| 38 | GPIO19 | Front pnl | Front panel I2C SCL | - | | |||
| N/C | | | | | | |||
| 42 | GPIO21 | Front pnl | Front panel I2C SDA | - | | |||
| 40 | GPIO3 | Serial | Debug pad, RX (flashing, logs) | - | | |||
| 41 | GPIO1 | Serial | Debug pad, TX (flashing, logs) | - | | |||
| 39 | GPIO22 | - | | IN/OUT | | |||
| 36 | GPIO23 | - | | IN/OUT | | |||
| GND | | Ground | Connected to ground | - | | |||
1. GPIO25 is connected to a 10k pull up resistor. This suggests that it might have some function in | |||
the lamp, but I have not found that function yet. If you find the actual use for this pin, or | |||
find that you can indeed repurpose it, then please let me know. | |||
1. Beware that GPIO15 outputs a PWM signal at boot. This might make the pin less useful for your use | |||
case. | |||
1. Often, GPIO2 is used for an on-board LED. Here, it is only connected to the debug pad. The pin is | |||
usable for I/O (I tested it), which is great because of the easy access of the debug pad. GPIO2 | |||
might only be used for testing purposes in the original firmware. | |||
1. The connected IC, using I2C address 0x10, looks a lot like an EEPROM, but this has yet to be | |||
confirmed. It uses a decicated I2C bus, separate from the I2C bus of the front panel. | |||
[This picture](../images/hardware/IC_on_I2C_GPIO1718.jpg) shows the IC. | |||
For more information on the use of pins on the ESP32 chip, please check out | |||
this [ESP32 pinout reference information](https://randomnerdtutorials.com/esp32-pinout-reference-gpios/). | |||
## Front panel | |||
<img src="../images/hardware/front_panel.jpg" width="150"> | |||
The front panel is a stand-alone component, with its own control chip (KungFu KF8TS2716). | |||
Communication between the ESP32 and the front panel is done using: | |||
- **An I2C bus** | |||
- the front panel is the I2C slave, the ESP32 is the I2C master | |||
(pardon the standard terminology, I am aware of the controversy) | |||
- the front panel device ID is 0x2C | |||
- SDA is connected to ESP32 pin GPIO21 | |||
- SCL is connected to ESP32 pin GPIO19 | |||
- **An interrupt data line to signal the ESP32 about new events** | |||
- this line is connected to ESP32 pin GPIO16 | |||
- the default state is HIGH | |||
- line is pulled LOW for at least 6 ms when a new event is available | |||
Commands can be written to and data can be read from the front panel component using I2C. The I2C | |||
protocol is fairly simple. All read and write operations uses 6 bytes of data. No register selection | |||
is done before reading or writing. | |||
The interrupt data line is used by the front panel, to signal the ESP32 that a new button or slider | |||
event is available. Further details on this can be found below. | |||
**Connection to the main board** | |||
The front panel is connected to the main board using a flat cable. The picture below shows the | |||
connector on the main board, including the functions of the cable pins: | |||
<img src="../images/hardware/front_panel_flat_cable_connection.jpg" width="400"> | |||
**Writing commands to the front panel** | |||
Commands can be written to the front panel at any time. | |||
The commands that are used by the original firmware are these: | |||
| Command | Byte sequence to send | | |||
|-----------------|-----------------------| | |||
| TURN PANEL ON | 02 03 5E 00 64 00 00 | | |||
| TURN PANEL OFF | 02 03 0C 00 64 00 00 | | |||
| SET LEVEL 1 | 02 03 5E 00 64 00 00 | | |||
| SET LEVEL 2 | 02 03 5F 00 64 00 00 | | |||
| SET LEVEL 3 | 02 03 5F 80 64 00 00 | | |||
| SET LEVEL 4 | 02 03 5F C0 64 00 00 | | |||
| SET LEVEL 5 | 02 03 5F E0 64 00 00 | | |||
| SET LEVEL 6 | 02 03 5F F0 64 00 00 | | |||
| SET LEVEL 7 | 02 03 5F F8 64 00 00 | | |||
| SET LEVEL 8 | 02 03 5F FC 64 00 00 | | |||
| SET LEVEL 9 | 02 03 5F FE 64 00 00 | | |||
| SET LEVEL 10 | 02 03 5F FF 64 00 00 | | |||
| READY FOR EVENT | 01 00 00 00 00 00 01 | | |||
*Note: The `READY FOR EVENT` command is only used when a new event is provided by the front panel. | |||
Information about this command can be found in the next section.* | |||
Further experimentation has uncovered that the LEDs of the front panel can be controlled | |||
individually. The original firmware does not use this feature, but I built support for it | |||
into the custom firmware, because it opens up some nice possibilities. | |||
How this works, is that the general format of the "set LEDs" command is: `02 03 XX XX 64 00 00`. | |||
The LEDs to enable are specified using the `XX XX` part. This is a 16 bit value, which can be | |||
constructed by bitwise OR-ing the following LED bit values: | |||
| LED to enable | Bit pattern | | |||
|---------------|-------------------| | |||
| POWER | 01001100 00000000 | | |||
| COLOR | 00011100 00000000 | | |||
| LED 1 | 00001110 00000000 | | |||
| LED 2 | 00001101 00000000 | | |||
| LED 3 | 00001100 10000000 | | |||
| LED 4 | 00001100 01000000 | | |||
| LED 5 | 00001100 00100000 | | |||
| LED 6 | 00001100 00010000 | | |||
| LED 7 | 00001100 00001000 | | |||
| LED 8 | 00001100 00000100 | | |||
| LED 9 | 00001100 00000010 | | |||
| LED 10 | 00001100 00000001 | | |||
LED 1 is the one closest to the power button. | |||
LED 10 is the one closest to the color button. | |||
**Reading events from the front panel** | |||
The types of events that can occur can be summarized as: | |||
- Touch or release the power button | |||
- Touch or release the color button | |||
- Touch or release the slider at a certain level | |||
Because the front panel is an I2C slave device, it cannot contact the ESP32 via I2C. Only an I2C | |||
master device can initiate communication. Therefore, when the front panel has a new event available, | |||
it will pull down the interrupt line for a short period of time, to signal the ESP32 about this new | |||
event. | |||
*Note that the ESP32 needs to poll the interrupt line at least at 667 Hz to be able to trustworthy | |||
detect the 6 ms signal. Unfortunately, the interrupt line does not wait for the ESP32 to respond to | |||
its signalling. The best way to handle signals from this line, is to use an actual interrupt | |||
handler.* | |||
After detecting this signal, the ESP32 must first write the "READY FOR EVENT" command (`01 00 00 00 | |||
00 00 01`) via I2C to the front panel. | |||
After the front panel has ACK'ed this command, the ESP32 can read 6 bytes, which will represent the | |||
event that occurred. | |||
Here's the mapping for the events and their corresponding byte sequences: | |||
| | Touch event | Release event | | |||
|-----------------|----------------------|----------------------| | |||
| POWER BUTTON | 04 04 01 00 01 01 03 | 04 04 01 00 01 02 04 | | |||
| COLOR BUTTON | 04 04 01 00 02 01 04 | 04 04 01 00 02 02 05 | | |||
| SLIDER LEVEL 1 | 04 04 01 00 03 16 1A | 04 04 01 00 04 16 1B | | |||
| SLIDER LEVEL 2 | 04 04 01 00 03 15 19 | 04 04 01 00 04 15 1A | | |||
| SLIDER LEVEL 3 | 04 04 01 00 03 14 18 | 04 04 01 00 04 14 19 | | |||
| SLIDER LEVEL 4 | 04 04 01 00 03 13 17 | 04 04 01 00 04 13 18 | | |||
| SLIDER LEVEL 5 | 04 04 01 00 03 12 16 | 04 04 01 00 04 12 17 | | |||
| SLIDER LEVEL 6 | 04 04 01 00 03 11 15 | 04 04 01 00 04 11 16 | | |||
| SLIDER LEVEL 7 | 04 04 01 00 03 10 14 | 04 04 01 00 04 10 15 | | |||
| SLIDER LEVEL 8 | 04 04 01 00 03 0F 13 | 04 04 01 00 04 0F 14 | | |||
| SLIDER LEVEL 9 | 04 04 01 00 03 0E 12 | 04 04 01 00 04 0E 13 | | |||
| SLIDER LEVEL 10 | 04 04 01 00 03 0D 11 | 04 04 01 00 04 0D 12 | | |||
| SLIDER LEVEL 11 | 04 04 01 00 03 0C 10 | 04 04 01 00 04 0C 11 | | |||
| SLIDER LEVEL 12 | 04 04 01 00 03 0B 0F | 04 04 01 00 04 0B 10 | | |||
| SLIDER LEVEL 13 | 04 04 01 00 03 0A 0E | 04 04 01 00 04 0A 0F | | |||
| SLIDER LEVEL 14 | 04 04 01 00 03 09 0D | 04 04 01 00 04 09 0E | | |||
| SLIDER LEVEL 15 | 04 04 01 00 03 08 0C | 04 04 01 00 04 08 0D | | |||
| SLIDER LEVEL 16 | 04 04 01 00 03 07 0B | 04 04 01 00 04 07 0C | | |||
| SLIDER LEVEL 17 | 04 04 01 00 03 06 0A | 04 04 01 00 04 06 0B | | |||
| SLIDER LEVEL 18 | 04 04 01 00 03 05 09 | 04 04 01 00 04 05 0A | | |||
| SLIDER LEVEL 19 | 04 04 01 00 03 04 08 | 04 04 01 00 04 04 09 | | |||
| SLIDER LEVEL 20 | 04 04 01 00 03 03 07 | 04 04 01 00 04 03 08 | | |||
| SLIDER LEVEL 21 | 04 04 01 00 03 02 06 | 04 04 01 00 04 02 07 | | |||
| SLIDER LEVEL 22 | 04 04 01 00 03 01 05 | 04 04 01 00 04 01 06 | | |||
**Behavior when more events come in than can be handled** | |||
The front panel does not queue events. When a new event occurs, before the previous event has be | |||
read by the ESP32, the new event will replace the old event and a new signal is sent over the | |||
interrupt line. | |||
The ESP32 can read the last event multiple times. It will not be cleared by the front panel after | |||
reading it. | |||
## Build requirements | |||
The ESP-WROOM-32D that is used for this lamp (and for various other Xiaomi devices), contains a | |||
single core CPU, even though the data sheet for ESP-WROOM-32D specifies a dual core CPU. Therefore, | |||
when flashing the device with a generic ESP32 build, you will end up with the following boot error: | |||
``` | |||
E (459) cpu_start: Running on single core chip, but application is built with dual core support. | |||
E (459) cpu_start: Please enable CONFIG_FREERTOS_UNICORE option in menuconfig. | |||
``` | |||
Another issue with a lot of these devices, is that the MAC address that is burnt into EFUSE does not | |||
match the CRC checksum that is also burnt into EFUSE. Using a generic ESP32 build, you will end up | |||
with the boot error: | |||
``` | |||
Base MAC address from BLK0 of EFUSE CRC error | |||
``` | |||
For these reasons, you must build the firmware using a taylored version of arduino-esp32. | |||
You can make use of the version [created by @pauln](https://github.com/pauln/arduino-esp32) | |||
or the version [created by @mmakaay](https://github.com/mmakaay/arduino-esp32-unicore-no-mac-crc). | |||
To make use of one of these in an ESPHome build, you'll have to provide a platform package | |||
definition for the PlatformIO build. Here's an example configuration that will work for these Xiaomi | |||
devices: | |||
```yaml | |||
esphome: | |||
name: my_device_name | |||
platform: ESP32 | |||
board: esp32doit-devkit-v1 | |||
platformio_options: | |||
platform: espressif32@3.2.0 | |||
platform_packages: |- | |||
framework-arduinoespressif32 @ https://github.com/mmakaay/arduino-esp32-unicore-no-mac-crc | |||
``` | |||
If you want to build your own platform package, then you can checkout | |||
the build scripts [by @mmakaay here](https://github.com/mmakaay/arduino-esp32-unicore-no-mac-crc-builder). | |||
## Original firmware | |||
Below, I have gathered some of the interesting boot messages from the original firmware. These | |||
messages are logged via the serial interface. | |||
**SPI Flash memory:** | |||
``` | |||
boot: SPI Flash RID : 0xB20B4 | |||
boot: SPI Flash MF : 0xB4 | |||
boot: SPI Flash ID : 0x200B | |||
boot: SPI Speed : 40MHz | |||
boot: SPI Mode : DIO | |||
boot: SPI Flash Size : 4MB | |||
``` | |||
**Partition table:** | |||
``` | |||
boot: Partition Table: | |||
boot: ## Label Usage Type ST Offset Length | |||
boot: 0 nvs WiFi data 01 02 00009000 00004000 | |||
boot: 1 otadata OTA data 01 00 0000d000 00002000 | |||
boot: 2 phy_init RF data 01 01 0000f000 00001000 | |||
boot: 3 miio_fw1 OTA app 00 10 00010000 001e0000 | |||
boot: 4 miio_fw2 OTA app 00 11 001f0000 001e0000 | |||
boot: 5 test test app 00 20 003d0000 00013000 | |||
boot: 6 mfi_p Unknown data 01 82 003e3000 00001000 | |||
boot: 7 factory_nvs WiFi data 01 02 003e4000 00004000 | |||
boot: 8 coredump Unknown data 01 03 003e8000 00010000 | |||
boot: 9 minvs Unknown data 01 fe 003f8000 00004000 | |||
boot: End of partition table | |||
``` | |||
**MIIO initialization:** | |||
``` | |||
_| _| _|_|_| _|_|_| _|_| | |||
_|_| _|_| _| _| _| _| | |||
_| _| _| _| _| _| _| | |||
_| _| _| _| _| _| | |||
_| _| _|_|_| _|_|_| _|_| | |||
08:00:00.200 [I] did=332985470 hostname=MiBedsideLamp2-7651 | |||
JENKINS BUILD NUMBER: N/A | |||
BUILD TIME: Sep 5 2019,07:12:39 | |||
BUILT BY: N/A | |||
MIIO APP VER: 2.0.6_0030 | |||
Setup ID: 95XJ | |||
Getting setup info from factory NVS | |||
MIIO MCU VER: | |||
MIIO DID: ********* | |||
MIIO WIFI MAC: ************ | |||
MIIO MODEL: yeelink.light.bslamp2 | |||
ARCH TYPE: esp32,0x0000a601 | |||
ARCH VER: d178b9b | |||
``` | |||
**Network initialized:** | |||
``` | |||
[20:27:05]08:00:04.180 [I] miio_net: Wifi station connected | |||
[20:27:05]Registering HomeKit web handlers | |||
[20:27:05]Announcing _hap._tcp mDNS service | |||
``` | |||
**Phoning home to the Mijia cloud:** | |||
``` | |||
ots: httpdns resolve start failed, -12 (ots_cloud_host_update,850) | |||
otu: Opened. | |||
ots: de.ots.io.mi.com resolved to 3.126.247.75. | |||
ots: ots connect 3.126.247.75::443... | |||
tls: connect to server Mijia Cloud, domain is 3.126.247.75, port is 443. | |||
tls: timeout[100]! mbedtls_ssl_handshake returned -0x6800 (d0_tls_open,369) | |||
tls: timeout[200]! mbedtls_ssl_handshake returned -0x6800 (d0_tls_open,369) | |||
tls: timeout[300]! mbedtls_ssl_handshake returned -0x6800 (d0_tls_open,369) | |||
tls: timeout[400]! mbedtls_ssl_handshake returned -0x6800 (d0_tls_open,369) | |||
ots: Connected. | |||
``` | |||
< [Flashing guide](flashing.md) | [Index](../README.md) | [Sponsoring](sponsoring.md) > |
@ -0,0 +1,73 @@ | |||
# Test plan | |||
This is the test plan that I follow for every release. I use it to ensure that no regression has | |||
been introduced. | |||
## Step 1: Preparation - build and flash the firmware | |||
* Use the latest stable release for ESPHome (to make sure that my code builds and works with that | |||
release). In my clone I make sure that I checked out the master branch and that it's up-to-date | |||
with the origin. | |||
* Remove the cached framework package: | |||
`/bin/rm -fR ~/.platformio/packages/framework-arduinoespressif32` | |||
* Copy [`example.yaml`](../example.yaml) to a clean build directory, and update the first two sections for the | |||
local setup (substitutions, wifi, api, ota). | |||
* Use `esphome example.yaml compile` to build the code. | |||
* Upload this code to the lamp. | |||
## Step 2: Perform the tests | |||
* Touch the power button --> the light must turn on. | |||
* Touch the power button again --> the light must turn off. | |||
* Touch the slider at any level --> the light must turn on, using the level at which the slider was | |||
touched as the brightness. The front panel illumination is turned on and shows the brightness | |||
level. | |||
* Touch the slider at various points and touch the slider while moving your finger up and down --> | |||
the brightness of the light and the brightness level on the front panel must follow the touches. | |||
* Touch and hold the power button --> the light must go into "night light" mode. This means that it | |||
will become very dim. The front panel illumination must be turned off. | |||
* Touch the slider at a random level --> the light must go back to its normal brightness levels and | |||
the front panel illumination must be turned on again. | |||
* Touch the color button a few times in a row --> the light must loop through the various preset | |||
light colors. | |||
* Touch and hold the color button --> the light must switch to a different preset group. There are | |||
two groups: one with rgb colors and one with white light colors, and holding the color button will | |||
switch between these two. | |||
* Touch the color button to select a light color, long touch it to switch to the other preset group, | |||
then long touch again to switch the preset group again -> the lamp must remember what light colors | |||
were active within the preset groups. If the red preset was selected, you switch to the white | |||
light group, select a different white preset and then go back to the rgb presets, then the light | |||
must become red again. | |||
* Go to Home Assistant and check: | |||
* if you can turn on/off the lamp | |||
* if you can select colors from the RGB color model | |||
* if you can select colors from the Color Temperature model | |||
* if you can set the brightness of the lamp | |||
* if selecting the lowest possible brightness results in the lamp | |||
switching to "night light" mode | |||
* if a transition from off to on with a long transition time works (e.g. 10s) | |||
* if a transition from on to off with a long transition time works | |||
* if a transition from night light to some bright light (e.g. 10s) can be | |||
interrupted by a transition back to night light (e.g. after 5 seconds), | |||
resulting in a transition back (not an instant drop to night light) | |||
## Step 3: Release the new version | |||
Only after performing these tests successfully, the new version can be released. | |||
Release steps are documented in [`releaseplan.md`](releaseplan.md). |
@ -0,0 +1,123 @@ | |||
# Upgrading to version 2021.10.0 | |||
Quite a few things have been changed in order to make the Xiaomi Bedside Lamp 2 firmware code compatible with | |||
ESPHome version 2021.10.0. As a result, configuration changes are needed to make the configuration work with | |||
the new code. | |||
There are basically two ways in which you can upgrade: | |||
1. Start a new configuration | |||
2. Upgrade your existing configuration | |||
## 1. Start a new configuration | |||
*This is definitely the simplest way*. | |||
Copy the new `example.yaml` configuration file from the GitHub repository and start a new configuration based | |||
on that. When this configuration is working, you can extend your configuration to tweak the configuration to | |||
your needs. | |||
## 2. Upgrade your existing configuration | |||
If you have done extensive customization, it might be easier to upgrade your existing configuration. Below | |||
you find a list of changse that have to be applied, and that should get your configuration ready for 2021.10.0. | |||
**Setup the following substitutions in your configuration file:** | |||
```yaml | |||
substitutions: | |||
name: bedside-lamp | |||
friendly_name: Bedside Lamp | |||
light_name: ${friendly_name} RGBWW Light | |||
light_mode_text_sensor_name: ${friendly_name} Light Mode | |||
default_transition_length: 800ms | |||
``` | |||
Other substitutions are not in use anymore, so you can delete them. | |||
**Modify the existing `external_components:` configuration to:** | |||
```yaml | |||
external_components: | |||
- source: | |||
type: git | |||
url: https://github.com/mmakaay/esphome-xiaomi_bslamp2 | |||
ref: release/2021.10.0 | |||
refresh: 60s | |||
``` | |||
**Add the new core configuration package:** | |||
```yaml | |||
packages: | |||
bslamp2: | |||
url: https://github.com/mmakaay/esphome-xiaomi_bslamp2 | |||
ref: release/2021.10.0 | |||
files: | |||
- packages/core.yaml | |||
refresh: 0s | |||
``` | |||
You can omit the ones that represent the required value already. | |||
**Fully remove the existing `esphome:` configuration section** | |||
Remove the following block from your configuration. The `core.yaml` configuration | |||
package provides the required configuration, so you don't need it in your | |||
configuration file anymore. | |||
```yaml | |||
esphome: | |||
name: ${name} | |||
... | |||
etc. | |||
``` | |||
**Modfy all instances of id's from a variable to a string value:** | |||
- `${id_light}` -> `my_light` | |||
- `${id_front_panel_illumination}` -> `my_front_panel_illumination` | |||
- `${id_light_mode}` -> `my_light_mode` | |||
- `${id_power_button}` -> `my_power_button` | |||
- `${id_color_button}` -> `my_color_button` | |||
- `${id_slider_level}` -> `my_slider_level` | |||
These are the identifiers that I will be using from now on in the configuration | |||
packages as well. By using these identifiers in all places, it will be easy to | |||
distribute configuration packages that can be added to your configuration. | |||
**Fully remove the front panel illumination output from the config** | |||
This configuration is already being setup as part of the `core.yaml` configuration package. | |||
When not removing it, you would get an error about the component being redefined. So simply | |||
remove this block from your configuration: | |||
```yaml | |||
output: | |||
- platform: xiaomi_bslamp2 | |||
id: my_front_panel_illumination | |||
``` | |||
**Optionally, modify the the name of the light component** | |||
For the name of the light, you can replace `${friendly_name} RGBWW Light` with | |||
the new `${light_name}` substitution variable. | |||
```yaml | |||
light: | |||
- platform: xiaomi_bslamp2 | |||
id: my_light | |||
name: ${light_name} | |||
... | |||
``` | |||
** Optionally, modify the name of the text sensor** | |||
Likewise, you can change the text sensor that publishes the current light mode as text. | |||
The name can be changed from `${fiendly_name} Light Mode` to `${light_mode_text_sensor_name}`. | |||
```yaml | |||
text_sensor: | |||
- platform: xiaomi_bslamp2 | |||
id: my_light_mode | |||
name: ${light_mode_text_sensor_name} | |||
``` |
@ -0,0 +1,35 @@ | |||
[Index](../README.md) | [Installation guide](installation.md) > | |||
# 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 | |||
[Index](../README.md) | [Installation guide](installation.md) > |