This project has two main objectives:
The NXP JN5169 microcontroller was chosen for its use in Xiaomi devices manufactured between 2017 and 2021. NXP offers detailed documentation, SDK source codes, and numerous examples, facilitating the development of custom Zigbee solutions. However, the wealth of information provided by NXP can be overwhelming for newcomers to Zigbee technology. To address this, a step-by-step tutorial was created to simplify the learning process.
The following devices are the target for this project:
The original Xiaomi Aqara QBKG12LM smart switch provides a very limited functionality. It cannot bind to other devices, does not handle multiple clicks, cannot work as a group client, and lacks many features that other modern smart switches provide. Also, stability of the original firmware is not always good.
The alternative firmware features are:
The code in this project is designed to be modular, allowing it to run on various hardware platforms, assuming they are based on the JN5169 microcontroller. Here are some of the boards that the code is compatible with.
The main focus of this project are the QBKG11LM and QBKG12LM switches (1-gang and 2-gang switches with a neutral line). Both models share the same PCB layout, differing only in the number of buttons and relays installed.
The board schematics have been reverse engineered and are detailed here. These devices are self-contained, featuring their own power supply, MCU, antenna circuits, buttons, LEDs, and relays. There's no need to modify any components of the mass-produced device for this project.
The MCU board has several pads designed for connecting a flashing tool.
The NXP JN5169 microcontroller can be programmed using a standard USB-UART adapter attached to these pads. To enter bootloader mode, the MISO line must be grounded during reset. An adapter like the shown below can be used for this purpose. The capacitor connected to the DTR pin can be skipped if you're only flashing the microcontroller and don't intend to conduct automated tests.
Warning!!! Do not connect USB-UART converter if the switch is connected to the mains power.
Alternatively, the MCU board can be separated from the power board. In this case it can be used as a development board with no risk of high voltage injury.
To avoid risking damage to an expensive Xiaomi device, you can use a more affordable development board equipped with the EBYTE E75-2G4M10S module.
This board features several LEDs and buttons, and supports programming and debugging through UART. It's suitable for learning Zigbee technology and developing most functionalities of a real device.
Prerequisites:
xmltodict
and pycryptodome
libraries installedBuild instructions:
git clone git@github.com:grafalex82/hellozigbee.git
)
git submodule update --init
)build
directorycd build
cmake -DTOOLCHAIN_PREFIX=<path to toolchain> ..
cmake --preset=default ..
-G "MinGW Makefiles"
to generate proper type of makefilesmingw32-make HelloZigbee.bin
to build a binary that can be flashed to the devicemingw32-make HelloZigbee.flash
to build and immediately flash the binarymingw32-make HelloZigbee.ota
to build a binary that can be used for OTA updates-DBOARD=QBKG12LM
to select target device (by default EBYTE E75-2G4M10S is selected)-DBUILD_NUMBER=123
to set the build number (build number uploaded via OTA must be higher than the current firmware build number)Note: the instructions above are for Windows and Linux. Mac support is pending. Feel free to contribute.
Flash instructions (Windows):
Program
buttonor (also Windows)
mingw32-make HelloZigbee.flash
or (any platform)
jn51xx_flasher.py -p <PORT> write <firmware.bin>
When joined the network, the zigbee2mqtt will list the device as unsupported. No features will be exposed.
To integrate the device follow these steps:
zigbee2mqtt/hellozigbee.js
to zigbee2mqtt configuration folder (next to configuration.yaml
)configuration.yaml
add the following entityexternal_converters:
- hellozigbee.js
After z2m restart the device features will be supported by zigbee2mqtt.
The firmware supports OTA update feature.
The OTA feature requires the firmware to be prepared in a special way, use the HelloZigbee.ota
build target to prepare a firmware suitable for OTA update. Also, the firmware must be configured with the build number which is higher than current firmware build number, otherwise firmware update process will reject the new firmware (use -DBUILD_NUMBER=xxx
cmake switch to set the build number).
The built firmware shall be uploaded to the zigbee2mqtt data folder, next to the configuration.yaml. In order to let zigbee2mqtt know about available firmware, an OTA index override file shall be created:
[
{
"url": "HelloZigbee.ota",
"force": true
}
]
Also add the path to this index file to the configuration.yaml:
ota:
zigbee_ota_override_index_location: index.json
The device will be listed on the Zigbee2mqtt OTA page. Click on the Check firmware update
will check the firmware availability, and offer to update the firmware. The Update firmware
button will start the update process.
Hello Zigbee firmware identifies itself in the same way as official Xiaomi Aqara firmwares do. Since first series of Xiaomi Aqara devices (including QBKG11LM and QBKG12LM) do not use firmware encryption and special protection, custom Hello Zigbee firmware can be uploaded to the mass produced device over the air.
To update from stock firmware to Hello Zigbee one the following index override file shall be used (use lumi.ctrl_ln2.aq1
for QBKG11LM):
[
{
"modelId": "lumi.ctrl_ln2.aq1",
"url": "HelloZigbee.ota",
"force": true
}
]
The build number of the new firmware shall be higher than one used in the stock firmware (the latest official version build number is 95).
In order to switch back from Hello Zigbee firmware to the stock one, a similar procedure can be used.
[ { "modelId": "hello.zigbee.QBKG12LM", "url": "https://github.com/Koenkk/zigbee-OTA/blob/master/images/Lumi/20230202185209_OTA_lumi.ctrl_ln2.aq1_0.0.0_0095_20220725_0B0798.ota", "force": true } ]
The device implements a common type of home automation devices. On the first start the device is not connected to the network. Press and hold both device buttons for 5 seconds in order to initiate the network joining. Make sure your network permits joining, otherwise the device is not able to join.
Once the device joined the network, zigbee2mqtt will start intervieweing the device, which will take up to 15 seconds. If the zigbee2mqtt external converter is installed, the z2m system will provide full access to the device features.
Device automatically tries to rejoin the network if network conditions change (e.g. parent/neighbour router no longer responds). Device joining and rejoining, as well as failure recovery is implemented using BDB component (a part of Zigbee SDK). The device performs several rejoin attempts before giving up. Pressing both device buttons for 5 seconds will force device to leave the network.
The device implements a typical smart switch functionality. By default it operates as a normal switch: when pressing a button toggles the LED. Since this is a smart switch, it will also report its state to the Zigbee2Mqtt , as well as accept On/Off/Toggle commands from the network.
But the default behavior can be customized using the custom On/Off Configuration Cluster.
The settings are:
Operating Mode
:
Server
- the endpoint maintains internal state, drives its LED/Relay, and generates reports on state change.Client
- the endpoint generates On/Off/Toggle commands to bound devices. Local LED/Relay is not used.Switch Mode
:
Toggle
- each button press toggles the LED. The action happens immediately when button pressed. This mode provides the fastest feedback.Momentary
- turns On LED when the button is pressed, and turns Off when released.Multifunction
- a smart switch mode, that allows fine tuning of the switch behavior, supports single/double/tripple/long press, and controlling dimming lights and shades. See other options below for more details.Switch actions
(applicable for Momentary
switch mode only):
onOff
- turns on the LED when the button is pressed, and turns it off when releasedoffOn
- turns off the LED when the button is pressed, and turns it on when releasedtoggle
- toggles the LED each time when button is pressed or releasedRelay mode
- applicable for all Multifunction
mode, and allow fine tuning of the device behavior, as well as assigning multiple actions to the device dependning on clicks count:
Unlinked
- with this option the internal LED (and relay actuator on the real smart switch) will be decoupled from the button. In this mode the button will generate logical signals to the network, while the LED will retain its state. At the same time it is possible to control the LED via network commands.front
- the relay toggles once the button is pressed. This mode provides the fastest feedback, compared to the other options below. The device will also generate a single press
action to the network.single
- the relay toggles once the button is quickly pressed once. The device will also generate a single press
action to the network. Note that the action triggers with a short delay to ensure there are no further clicks happening.double
- the relay toggles once the button is quickly pressed twice. The device will also generate a double press
action to the network. Note that the action triggers with a short delay to ensure there are no further clicks happening.tripple
- the relay toggles once the button is quickly pressed 3 times. The device will also generate a tripple press
action to the network. long
- the relay toggles once the button is pressed, and remains pressed for some time (typically a 0.5s). The device will also generate a long press
action to the network, and the release
action when button is finallyreleased.Long Press Mode
is a special setting for the Long Press action (when the button is pressed and hold for at least 0.5s). This setting allows adding dimming light control to the button. Note, that this function requires binding the button to the dimming light device:
None
- no additional functionality (except for emiting long press
action to the network) is triggeredlevelCtrlUp
- when the button is pressed, the LevelCtrl cluster MoveUpWithOnOff
command is emitted. When the button is released, the LevelCtrl cluster Stop
command is emitted to stop previously started movementlevelCtrlDown
- Similar to previous, but the MoveDownWithOnOff
command will be used.Interlock Mode
is a mode when both switch buttons work together, and do not allow setting both endpoints to ON state simultaneously:
None
- interlock mode is deactivatedMutual Exclusion
- Prevents two endpoints to be in ON state simultaneously. When second endpoint gets ON, previously active endpoint gets OFF. This mode allows setting both endpoints to OFF state.Opposite
- the two endpoints are also in opposite state. If one of them goes ON, another goes OFF, and vice versa.Max Pause
- a maximum time between button clicks so that consecutive clicks are consodered as a part of a multi-click action.Min Long Press
- a minimum time of the button press before emitting a long press
actionThe device also support a few handy functions:
A special behavior is implemented, depending on whether device is bound or not:
The project functionality comes with a comprehensive set of automated tests, covering main functionality of the device. These tests help keeping the code healthy regardless of changes being made.
To run tests:
tests\pytest.ini
file, specify parameters applicable for your setup (COM port, zigbee2mqtt address, device name, target board name, etc)cd tests
pytest
The -o
pytest option can be used to override default pytest.ini settings:
-o device_name=XXXXXX
to specify zigbee2mqtt device name-o target_board=QBKG12LM
to set the proper device type-o port=COM10
to set the COM portAll this code is explained in very detail in the Hello Zigbee article series
This project is being developed for free as a pet project. At the same time you may consider supporting the project with a small donate.