
Here I would like to show how we reverse-engineered an interesting Tuya lamp, based on individually addressable LEDs that can produce various types of animations, such as "waterfall", "fire", etc. The goal of the topic will be to free this lamp from the cloud and replace the original software with our still developed open source equivalent that can operate 100% locally and anonymously.
I've worked on this device together with my friend from Serbia - @DeDaMrAz. He has bought the lamp and he operated it, while I dealt with the firmware side.
Purchase of the device
We ordered the lamp at the request of one user who needed help in freeing it from the cloud, but you can search for it under the phrase Smart Wall Light taya APP RGB Outdoor Porch Colored light Party intelligent remote control Decoration Lighting holiday Wall Lamp . There are many versions to choose from, the longer the strap, the higher the price:

We paid €49.02 for the selected version.
The structure of this lamp is quite specific, the strap is mounted around it. This video from the manufacturer shows it better:
This lamp is controlled via WiFi (Tuya application) and by a remote control, in the version we purchased it is an RF remote control.
Action plan
Our goal is to free the device in question from the cloud, and this consists of individual tasks:
- first we need to examine how this device is built
- in this case, we also need to capture its TuyaMCU communication packets, because here the device consists of a separate WiFi module (which we will change the firmware for) for communication with the outside world and an MCU for controlling addressable LEDs and receiving IR codes from the remote control
- we need to analyze the captured packets to know which dpIDs (TuyaMCU variable identifiers) are responsible for what, we have our own tool for this
- then we need to upload our firmware to the WiFi module through our flasher
- then we need to script OBK to emulate packets sent by TuyaMCU by the original Tuya software and give it some sensible user interface on the OBK panel
Here are some related topics that will explain some of the concepts I use:
- TuyaMCU protocol - communication between the microcontroller and the WiFi module
- AnalyzerTuyaMCU - UART packet decoder for Tuya devices - dpID detector
First impression
The device consists of a front with LEDs, a power supply and a controller module:


You need to use a bit of force to get inside:

The main PCB is hidden in the heat shrink sleeve:





The LEDs are 12V, not WS2812B, but SM16703:

After connecting, the device flashes in pairing mode:
You can easily connect to the Tuya application:

NOTE: pairing is started by pressing the MUSIC button on the remote control.
After connecting the device to Tuya, we captured UART packets and at the same time noted what operation was performed. So, for example, we turn on the capture, turn on the light, and save the results. Then we turn on capture, change the brightness, and save the results. And so with every operation.
Captured communication packets from TuyaMCU
Here are the collected packages, they can be easily analyzed in the mentioned analyzer:
https://www.elektroda.pl/rtvforum/topic3970199.html
https://github.com/openshwprojects/TuyaMCUAnalyzer
Reboot after pairing:
//S 09/03/23 20:46:41 WiFi sent:
55AA00000000FF
//R 09/03/23 20:46:42 WiFi received:
55AA030000010104
//R 09/03/23 20:46:44 WiFi received:
55AA030000010104
//S 09/03/23 20:46:44 WiFi sent:
55AA00000000FF
//S 09/03/23 20:46:58 WiFi sent:
55AA00000000FF
//R 09/03/23 20:46:58 WiFi received:
55AA030000010104
//S 09/03/23 20:46:59 WiFi sent:
55AA0001000000
//R 09/03/23 20:46:59 WiFi received:
55AA0301002A7B2270223A2263756F666E346863726F616D746E6833222C2276223A22312E302E30222C226D223A307D35
//S 09/03/23 20:47:00 WiFi sent:
55AA0002000001
//R 09/03/23 20:47:00 WiFi received:
55AA0302000004
//S 09/03/23 20:47:01 WiFi sent:
55AA0008000007
//R 09/03/23 20:47:01 WiFi received:
55AA03070005140100010125
//R 09/03/23 20:47:02 WiFi received:
55AA03070005150400010129
//R 09/03/23 20:47:02 WiFi received:
55AA030700081A02000400001F4090
//R 09/03/23 20:47:02 WiFi received:
55AA030700093300000500000101F440
//R 09/03/23 20:47:02 WiFi received:
55AA030700093400000500000101F441
//R 09/03/23 20:47:02 WiFi received:
55AA03070008350200040000012C79
//S 09/03/23 20:47:02 WiFi sent:
55AA00000000FF
//R 09/03/23 20:47:03 WiFi received:
55AA030000010104
//S 09/03/23 20:47:03 WiFi sent:
55AA000300010407
//R 09/03/23 20:47:04 WiFi received:
55AA0303000005
//S 09/03/23 20:47:06 WiFi sent:
55AA00000000FF
//R 09/03/23 20:47:07 WiFi received:
55AA030000010104
//S 09/03/23 20:47:10 WiFi sent:
55AA00000000FF
//R 09/03/23 20:47:11 WiFi received:
55AA030000010104
//S 09/03/23 20:47:14 WiFi sent:
55AA00000000FF
//R 09/03/23 20:47:15 WiFi received:
55AA030000010104
//S 09/03/23 20:47:18 WiFi sent:
55AA00000000FF
//R 09/03/23 20:47:19 WiFi received:
55AA030000010104
//S 09/03/23 20:47:22 WiFi sent:
55AA00000000FF
//R 09/03/23 20:47:23 WiFi received:
55AA030000010104
//S 09/03/23 20:47:26 WiFi sent:
55AA00000000FF
//R 09/03/23 20:47:27 WiFi received:
55AA030000010104
//S 09/03/23 20:47:30 WiFi sent:
55AA00000000FF
//R 09/03/23 20:47:31 WiFi received:
55AA030000010104
//S 09/03/23 20:47:34 WiFi sent:
55AA00000000FF
//R 09/03/23 20:47:35 WiFi received:
55AA030000010104
//S 09/03/23 20:47:38 WiFi sent:
55AA00000000FF
//R 09/03/23 20:47:39 WiFi received:
55AA030000010104
//S 09/03/23 20:47:42 WiFi sent:
55AA00000000FF
//R 09/03/23 20:47:43 WiFi received:
55AA030000010104
Here we basically get to know the modules for the first time, report the device version, WiFi status, etc. Additionally, the initial values of the variables are also sent, but for now it's hard to guess what they mean, we'll deal with that in a moment:

Blue LED Color:
//S 09/03/23 19:13:29 WiFi sent:
55AA000600191C030015313030643330336330303365383030303030303030F0
//S 09/03/23 19:13:29 WiFi sent:
55AA00060005150400010125
//R 09/03/23 19:13:29 WiFi received:
55AA030700191C030015313030643330336330303365383030303030303030F4
//S 09/03/23 19:13:29 WiFi sent:
55AA000600101803000C30306433303363303033653829
//R 09/03/23 19:13:29 WiFi received:
55AA03070005150400010129
//R 09/03/23 19:13:29 WiFi received:
55AA030700101803000C3030643330336330303365382D
The analyzer already shows that dpID 24 is responsible for the color, although it is a string type. Many Tuya devices have this color coded and also in dpID 24:

Additionally, the analyzer reads the color correctly and shows it in the form of a colored square.
Changing the brightness of the chase animation:
//R 09/03/23 21:52:04 WiFi received:55AA0307000515040001022A//R 09/03/23 21:52:04 WiFi received:55AA030700201903001C3032306430643030303031343033653830336538303030303030303090//R 09/03/23 21:52:07 WiFi received:55AA030000010104//R 09/03/23 21:52:08 WiFi received:55AA0307000515040001022A//R 09/03/23 21:52:08 WiFi received:55AA030700201903001C3032306430643030303031343033653830303061303030303030303081
100-0
//R 09/03/23 21:52:33 WiFi received:55AA0307000515040001022A//R 09/03/23 21:52:34 WiFi received:55AA030700201903001C3032306430643030303031343033653830303061303030303030303081//R 09/03/23 21:52:37 WiFi received:55AA030000010104//R 09/03/23 21:52:38 WiFi received:55AA0307000515040001022A//R 09/03/23 21:52:39 WiFi received:55AA030700201903001C30323064306430303030313430336538303063633030303030303030B6
0-20
//R 09/03/23 21:53:15 WiFi received:55AA0307000515040001022A//R 09/03/23 21:53:15 WiFi received:55AA030700201903001C3032306430643030303031343033653830306333303030303030303086//R 09/03/23 21:53:19 WiFi received:55AA0307000515040001022A//R 09/03/23 21:53:19 WiFi received:55AA030700201903001C303230643064303030303134303365383031386630303030303030308F//R 09/03/23 21:53:22 WiFi received:55AA030000010104
20-40
//R 09/03/23 21:53:44 WiFi received:55AA0307000515040001022A//R 09/03/23 21:53:44 WiFi received:55AA030700201903001C303230643064303030303134303365383031386630303030303030308F//R 09/03/23 21:53:49 WiFi received:55AA0307000515040001022A//R 09/03/23 21:53:49 WiFi received:55AA030700201903001C303230643064303030303134303365383032356430303030303030308B//R 09/03/23 21:53:52 WiFi received:55AA030000010104
40-60
//R 09/03/23 21:54:06 WiFi received:55AA0307000515040001022A//R 09/03/23 21:54:06 WiFi received:55AA030700201903001C303230643064303030303134303365383032356430303030303030308B//R 09/03/23 21:54:07 WiFi received:55AA030000010104//R 09/03/23 21:54:10 WiFi received:55AA0307000515040001022A//R 09/03/23 21:54:10 WiFi received:55AA030700201903001C3032306430643030303031343033653830333231303030303030303056
60-80
//R 09/03/23 21:54:28 WiFi received:55AA0307000515040001022A//R 09/03/23 21:54:28 WiFi received:55AA030700201903001C3032306430643030303031343033653830333231303030303030303056//R 09/03/23 21:54:31 WiFi received:55AA0307000515040001022A//R 09/03/23 21:54:31 WiFi received:55AA030700201903001C3032306430643030303031343033653830336538303030303030303090
80-100
Here we see that, surprisingly, there is no separate dpID responsible for brightness, it is coded in color... this dpID 21 here is the operating mode.

So dpID 25 is an animation described as a string, its length may vary. We saw earlier that the regular color is dpID 24
Chase animation:
//R 09/03/23 18:29:16 WiFi received:
55AA0307000515040001022A
//S 09/03/23 18:29:16 WiFi sent:
55AA00060005150400010226
//S 09/03/23 18:29:16 WiFi sent:
55AA000600201903001C303230653064303030303134303365383033653830303030303030308D
//R 09/03/23 18:29:16 WiFi received:
55AA030700201903001C3032306530643030303031343033653830336538303030303030303091
//S 09/03/23 18:29:17 WiFi sent:
55AA00000000FF
//R 09/03/23 18:29:18 WiFi received:
55AA030000010104
Here again you can see that the operating mode is set (dpID 21) and the animation is sent in dpID 25:

Collision animation:
//S 09/03/23 18:33:07 WiFi sent:
55AA00060005150400010226
//R 09/03/23 18:33:07 WiFi received:
55AA0307000515040001022A
//S 09/03/23 18:33:07 WiFi sent:
55AA000600A21903009E303734363436303230303030303365383033653830303030303030303436343630323030373830336538303365383030303030303030343634363032303066303033653830336538303030303030303034363436303230303364303365383033653830303030303030303436343630323030616530336538303365383030303030303030343634363032303131333033653830336538303030303030303073
//R 09/03/23 18:33:07 WiFi received:
55AA030700A21903009E303734363436303230303030303365383033653830303030303030303436343630323030373830336538303365383030303030303030343634363032303066303033653830336538303030303030303034363436303230303364303365383033653830303030303030303436343630323030616530336538303365383030303030303030343634363032303131333033653830336538303030303030303077
Basically as above, without comment, all animations go through dpID 25.
Draw curtain animation:
//S 09/03/23 18:28:42 WiFi sent:
55AA00060005150400010226
//R 09/03/23 18:28:42 WiFi received:
55AA0307000515040001022A
//S 09/03/23 18:28:42 WiFi sent:
55AA000600201903001C30303065306430303030326530336538303263633030303030303030E5
//R 09/03/23 18:28:42 WiFi received:
55AA030700201903001C30303065306430303030326530336538303263633030303030303030E9
//S 09/03/23 18:28:47 WiFi sent:
55AA00000000FF
//R 09/03/23 18:28:47 WiFi received:
55AA030000010104
Changing the brightness of a fireworks animation:
//R 09/03/23 22:04:53 WiFi received:
55AA030000010104
//R 09/03/23 22:04:56 WiFi received:
55AA0307000515040001022A
//R 09/03/23 22:04:56 WiFi received:
55AA030700A21903009E30353436343630313030303030336538303365383030303030303030343634363031303037383033653830336538303030303030303034363436303130306630303365383033653830303030303030303436343630313030336430336538303365383030303030303030343634363031303061653033653830336538303030303030303034363436303130313133303365383033653830303030303030306F
//R 09/03/23 22:05:02 WiFi received:
55AA0307000515040001022A
//R 09/03/23 22:05:02 WiFi received:
55AA030700A21903009E303534363436303130303030303365383030306130303030303030303436343630313030373830336538303030613030303030303030343634363031303066303033653830303061303030303030303034363436303130303364303365383030306130303030303030303436343630313030616530336538303030613030303030303030343634363031303131333033653830303061303030303030303015
//R 09/03/23 22:05:08 WiFi received:
55AA030000010104
//R 09/03/23 22:05:09 WiFi received:
55AA0307000515040001022A
//R 09/03/23 22:05:09 WiFi received:
55AA030700A21903009E303534363436303130303030303365383031323930303030303030303436343630313030373830336538303132393030303030303030343634363031303066303033653830313239303030303030303034363436303130303364303365383031323930303030303030303436343630313030616530336538303132393030303030303030343634363031303131333033653830313239303030303030303037
//R 09/03/23 22:05:13 WiFi received:
55AA0307000515040001022A
//R 09/03/23 22:05:13 WiFi received:
55AA030700A21903009E303534363436303130303030303365383032353730303030303030303436343630313030373830336538303235373030303030303030343634363031303066303033653830323537303030303030303034363436303130303364303365383032353730303030303030303436343630313030616530336538303235373030303030303030343634363031303131333033653830323537303030303030303043
//R 09/03/23 22:05:16 WiFi received:
55AA0307000515040001022A
//R 09/03/23 22:05:16 WiFi received:
55AA030700A21903009E303534363436303130303030303365383033316530303030303030303436343630313030373830336538303331653030303030303030343634363031303066303033653830333165303030303030303034363436303130303364303365383033316530303030303030303436343630313030616530336538303331653030303030303030343634363031303131333033653830333165303030303030303045
//R 09/03/23 22:05:18 WiFi received:
55AA0307000515040001022A
//R 09/03/23 22:05:18 WiFi received:
55AA030700A21903009E30353436343630313030303030336538303365383030303030303030343634363031303037383033653830336538303030303030303034363436303130306630303365383033653830303030303030303436343630313030336430336538303365383030303030303030343634363031303061653033653830336538303030303030303034363436303130313133303365383033653830303030303030306F
//R 09/03/23 22:05:23 WiFi received:
55AA030000010104
fireworks scene 100-0-30-60-85-100
fireworks animation:
//S 09/03/23 18:32:41 WiFi sent:
55AA00060005150400010226
//R 09/03/23 18:32:41 WiFi received:
55AA0307000515040001022A
//S 09/03/23 18:32:41 WiFi sent:
55AA000600A21903009E30353436343630313030303030336538303365383030303030303030343634363031303037383033653830336538303030303030303034363436303130306630303365383033653830303030303030303436343630313030336430336538303365383030303030303030343634363031303061653033653830336538303030303030303034363436303130313133303365383033653830303030303030306B
//R 09/03/23 18:32:41 WiFi received:
55AA030700A21903009E30353436343630313030303030336538303365383030303030303030343634363031303037383033653830336538303030303030303034363436303130306630303365383033653830303030303030303436343630313030336430336538303365383030303030303030343634363031303061653033653830336538303030303030303034363436303130313133303365383033653830303030303030306F
Green color setting:
//S 09/03/23 19:13:02 WiFi sent:
55AA000600191C030015313030366430333932303365383030303030303030CB
//R 09/03/23 19:13:02 WiFi received:
55AA030700191C030015313030366430333932303365383030303030303030CF
//S 09/03/23 19:13:02 WiFi sent:
55AA00060005150400010125
//S 09/03/23 19:13:02 WiFi sent:
55AA000600101803000C30303664303339323033653804
//R 09/03/23 19:13:02 WiFi received:
55AA03070005150400010129
//R 09/03/23 19:13:02 WiFi received:
55AA030700101803000C30303664303339323033653808
//S 09/03/23 19:13:03 WiFi sent:
55AA00000000FF
//R 09/03/23 19:13:03 WiFi received:
55AA030000010104
Pile up animation:
//S 09/03/23 18:32:02 WiFi sent:
55AA00000000FF
//R 09/03/23 18:32:03 WiFi received:
55AA030000010104
//S 09/03/23 18:32:03 WiFi sent:
55AA00060005150400010226
//S 09/03/23 18:32:03 WiFi sent:
55AA000600201903001C3031306530643030303038343030303030336538303030303030303053
//R 09/03/23 18:32:03 WiFi received:
55AA0307000515040001022A
//R 09/03/23 18:32:03 WiFi received:
55AA030700201903001C3031306530643030303038343030303030336538303030303030303057
rainbow animation:
//R 09/03/23 18:30:20 WiFi received:
55AA0307000515040001022A
//S 09/03/23 18:30:20 WiFi sent:
55AA00060005150400010226
//S 09/03/23 18:30:20 WiFi sent:
55AA00060054190300503036343634363031303030303033653830336538303030303030303034363436303130303738303365383033653830303030303030303436343630313030663030336538303365383030303030303030CF
//R 09/03/23 18:30:20 WiFi received:
55AA03070054190300503036343634363031303030303033653830336538303030303030303034363436303130303738303365383033653830303030303030303436343630313030663030336538303365383030303030303030D3
water animation:
//S 09/03/23 18:32:20 WiFi sent:
55AA00060005150400010226
//S 09/03/23 18:32:20 WiFi sent:
55AA000600201903001C303330653064303030306538303338333033316330303030303030308B
//R 09/03/23 18:32:20 WiFi received:
55AA0307000515040001022A
//R 09/03/23 18:32:20 WiFi received:
55AA030700201903001C303330653064303030306538303338333033316330303030303030308F
White color, brightness change from 0 to 100:
//S 09/03/23 19:17:26 WiFi sent:
55AA00060005150400010125
//R 09/03/23 19:17:26 WiFi received:
55AA03070005150400010129
//S 09/03/23 19:17:26 WiFi sent:
55AA000600101803000C303033643030333030336538F6
//R 09/03/23 19:17:26 WiFi received:
55AA030700101803000C303033643030333030336538FA
As I wrote earlier, brightness is encoded in color, dpID 21 is the operating mode (1 is pure color):

White color, brightness change from 100 to 0:
//S 09/03/23 19:16:48 WiFi sent:
55AA00000000FF
//R 09/03/23 19:16:48 WiFi received:
55AA030000010104
//S 09/03/23 19:16:50 WiFi sent:
55AA00060005150400010125
//S 09/03/23 19:16:50 WiFi sent:
55AA000600101803000C303033643030333030303061E7
//R 09/03/23 19:16:50 WiFi received:
55AA03070005150400010129
//R 09/03/23 19:16:50 WiFi received:
55AA030700101803000C303033643030333030303061EB
As I wrote earlier, brightness is encoded in color, dpID 21 is the operating mode (1 is pure color):

White color:
//S 09/03/23 19:16:15 WiFi sent:
55AA000600191C030015313030336430303330303365383030303030303030BD
//S 09/03/23 19:16:15 WiFi sent:
55AA00060005150400010125
//R 09/03/23 19:16:15 WiFi received:
55AA030700191C030015313030336430303330303365383030303030303030C1
//S 09/03/23 19:16:15 WiFi sent:
55AA000600101803000C303033643030333030336538F6
//R 09/03/23 19:16:15 WiFi received:
55AA03070005150400010129
//R 09/03/23 19:16:16 WiFi received:
55AA030700101803000C303033643030333030336538FA
//S 09/03/23 19:16:18 WiFi sent:
55AA00000000FF
//R 09/03/23 19:16:18 WiFi received:
55AA030000010104
Yellow:
//S 09/03/23 19:05:59 WiFi sent:
55AA000600191C03001531303033633033626230336538303030303030303020
//R 09/03/23 19:05:59 WiFi received:
55AA030700191C03001531303033633033626230336538303030303030303024
//S 09/03/23 19:05:59 WiFi sent:
55AA00060005150400010125
//S 09/03/23 19:05:59 WiFi sent:
55AA000600101803000C30303363303362623033653859
//R 09/03/23 19:05:59 WiFi received:
55AA03070005150400010129
//R 09/03/23 19:05:59 WiFi received:
55AA030700101803000C3030336330336262303365385D
//S 09/03/23 19:06:03 WiFi sent:
55AA00000000FF
//R 09/03/23 19:06:03 WiFi received:
55AA030000010104
Probably no surprises:

Packet analysis results
After analyzing the collected data, we reached the following conclusions:
- dpID 24 - color in the Tuya format, as a string, HSV, or more precisely in the 12-character format, where we have the value H, S and V, H in the range from 0 to 360, the rest from 0 to 1000, all as ASCII hex, i.e. generated as follows:
Code: C / C++
- dpID 25 - string of different length, somehow encoded animation or its ID (it's hard for us to say at the moment)
- dpID 21 - operating mode, either clear color or animation, 1 is color, 2 is animation, as far as I know, the value 0 would be CW (white temperature)
- dpID 20 - on or off, turning on or off the LEDs
OpenBeken configuration
OpenBeken already supported TuyaMCU base:
OpenBeken and dimmer on TuyaMCU - configuration and demonstration
Radar with WiFi - TuyaMCU, OpenBeken, own configuration page hosted on the device
Most of them were already implemented. Basically, we didn't even have to write C code to add support for this device, we just had to script the dpID role inside in OBK.
Therefore, we have prepared the autoexec.bat script, here I will discuss it one by one:
startDriver TuyaMCU
tuyaMcu_defWiFiState 4
tuyaMcu_setBaudRate 115200
The above section of the OBK script enables the TuyaMCU driver, sets the default WiFI state to 4 (according to the Tuya documentation it is "connected to cloud", so that the MCU thinks it is fully paired and sets the baud rate of TuyaMCU communication.
tuyaMCU_setupLED 24 1
The above code snippet runs the TuyaMCU LED driver on dpID 24, the first color format.
startDriver httpButtons
setButtonEnabled 0 1
setButtonLabel 0 "Music mode"
setButtonCommand 0 "tuyaMcu_sendState 21 4 3"
startDriver httpButtons
setButtonEnabled 1 1
setButtonLabel 1 "Light mode"
setButtonCommand 1 "tuyaMcu_sendState 21 4 1"
setButtonEnabled 2 1
setButtonLabel 2 "Curtain"
setButtonCommand 2 "startScript autoexec.bat do_cur"
setButtonEnabled 3 1
setButtonLabel 3 "Collision"
setButtonCommand 3 "startScript autoexec.bat do_col"
setButtonEnabled 4 1
setButtonLabel 4 "Rainbow"
setButtonCommand 4 "startScript autoexec.bat do_rai"
setButtonEnabled 5 1
setButtonLabel 5 "Pile"
setButtonCommand 5 "startScript autoexec.bat do_pil"
setButtonEnabled 6 1
setButtonLabel 6 "Firework"
setButtonCommand 6 "startScript autoexec.bat do_fir"
setButtonEnabled 7 1
setButtonLabel 7 "Chase"
setButtonCommand 7 "startScript autoexec.bat do_chase"
// stop execution
return
The above fragment uses the OpenBeken scriptable button driver to create buttons that trigger animations, or more precisely, scripts that play animations, which I will show later. OpenBeken is highly scriptable, so adding a button does not require recompiling the firmware at all.
do_chase:
tuyaMcu_sendState 21 4 2
delay_s 0.1
tuyaMcu_sendState 25 3 020e0d00001403e803e800000000
return
do_cur:
tuyaMcu_sendState 21 4 2
delay_s 0.1
tuyaMcu_sendState 25 3 000e0d00002e03e802cc00000000
return
do_col:
tuyaMcu_sendState 21 4 2
delay_s 0.1
tuyaMcu_sendState 25 3 07464602000003e803e800000000464602007803e803e80000000046460200f003e803e800000000464602003d03e803e80000000046460200ae03e803e800000000464602011303e803e800000000
return
do_rai:
tuyaMcu_sendState 21 4 2
delay_s 0.1
tuyaMcu_sendState 25 3 06464601000003e803e800000000464601007803e803e80000000046460100f003e803e800000000
return
do_pil:
tuyaMcu_sendState 21 4 2
delay_s 0.1
tuyaMcu_sendState 25 3 010e0d000084000003e800000000
return
do_fir:
tuyaMcu_sendState 21 4 2
delay_s 0.1
tuyaMcu_sendState 25 3 05464601000003e803e800000000464601007803e803e80000000046460100f003e803e800000000464601003d03e803e80000000046460100ae03e803e800000000464601011303e803e800000000
return
The above code fragment is the implementation of the animation on the OBK side, here we basically only send packages that trigger a given animation to the MCU. These are the exact same packets we captured earlier. Additionally, we also have to send the dpID value 21, value 2, i.e. animation mode.
If you have any questions about what particular commands are, please refer to:
https://github.com/openshwprojects/OpenBK7231T_App/blob/main/docs/commands.md
Modification - adding temperature measurement and physical buttons
A firmware change does not only have to reflect the original functionality of the device. It can also add new ones. Therefore, we decided to expand this lamp a bit by adding buttons and a temperature sensor.
We used MCP9808 as the temperature sensor : :
// startDriver MCP9808 [ClkPin] [DatPin] [OptionalTargetChannel]
startDriver MCP9808 7 8 1
MCP9808_Adr 0x30
MCP9808_Cycle 1
The above code fragment configures this sensor on given GPIO pins and sets its address. Communication takes place via I2C. The cycle is 1 second, measurement every second. The sensor configured in this way is also visible in Home Assistant after execution Home Assistant Discovery .
Additionally, we also connected regular physical buttons to a free GPIO, or more precisely, between the GPIO and ground.
In OBK, in the GPIO configuration panel, we simply chose the roles that suited us:
- Button - to switch the power state
- Button_NextDimmer - brightness level control
- Button_NextColor - color change
You could also use one button with multiple functions (double-click and long press): SmartButtonForLEDs
Additionally, we scripted the animation change button, gave it the Btn_ScriptOnly role and prepared the following script:
// use choice to choose effect by index stored in $CH10
alias do_chosen_effect Choice $CH10 cmd_cur cmd_col cmd_rai cmd_pil cmd_fir cmd_chase
// when click on Btn_ScriptOnly on P24 happens, add 1 to $CH10 (wrap to 0-5) and do effect
addEventHandler OnClick 24 backlog addChannel 10 1 0 4 1; do_chosen_effect
The above script uses channel 10 as a variable representing the current animation index, scrolls and loops it (see the addChannel documentation) and then uses the Choice command to fire the selected animation.
You can read about buttons (both general purpose and "specialized" ones such as color change) in OBK here:
https://github.com/openshwprojects/OpenBK7231T_App/blob/main/docs/ioRoles.md
Ultimately, this is what the OBK panel of this device looks like:

You can see the temperature reading from the MCP9808 and the scripted animation buttons mentioned above.
And the physical buttons work as in the video:
Summary
The controller of this device consists separately of a WiFi module that communicates with the outside world, and an MCU that controls the LEDs and supports the RF receiver from the remote control. The WiFi module communicates with the MCU via the TuyaMCU protocol on the UART 1 port. To free this device from the cloud, simply change the WiFi module firmware to OBK and script them appropriately. We didn't flash the MCU at all. Changing the firmware also allowed us to expand the functionality of the device to include temperature measurement and physical buttons, which I consider necessary. Why do we need an LED strip that can only be controlled by phone?
After our changes, the device works fully locally, does not connect to the manufacturer's servers and can be controlled from Home Assistant, although at the moment the animation trigger must be written manually in Yaml by sending the appropriate command to MQTT. The rest works through Home Assistant Discovery in OBK.
That's it for now, I will finally add that we are also working on another device that is also based on individually addressable LEDs, but in this case the strip is connected directly to the BK7231, there is no TuyaMCU used there. This means that we will soon be running the WS2812B driver on the BK, most likely based on SPI, but more about that another time...
Cool? Ranking DIY Helpful post? Buy me a coffee.