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