logo elektroda
logo elektroda
X
logo elektroda

BK7231N, TuyaMCU and individually addressable LEDs - effects control, adding buttons, thermometer

p.kaczmarek2 2355 2

TL;DR

  • A BK7231N-based Tuya wall lamp with 12V SM16703 addressable LEDs, WiFi control, and an RF remote was reverse-engineered and freed from the cloud.
  • The WiFi module was flashed with OpenBeken, while the separate MCU was left untouched; TuyaMCU UART traffic was sniffed to map dpIDs 20, 21, 24, and 25.
  • The lamp cost €49.02, and the TuyaMCU link runs at 115200 baud.
  • OpenBeken now reproduces color and animation effects locally, adds MQTT-driven effect buttons, and exposes a MCP9808 temperature sensor plus physical button controls.
  • Home Assistant integration works through discovery, but animation triggers still need manual YAML/MQTT commands.
Generated by the language model.
ADVERTISEMENT
Treść została przetłumaczona polish » english Zobacz oryginalną wersję tematu
📢 Listen (AI):
  • Tuya LED lamp undergoing reverse engineering process.
    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:
    Product display of Smart Wall Light with Tuya app and remote control.
    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:
    Tuya lamp module and remote control on a desk.
    Person holding a Tuya LED controller with WiFi label and QR code.
    You need to use a bit of force to get inside:
    Partially opened Tuya lamp casing with visible interior.
    The main PCB is hidden in the heat shrink sleeve:
    Tuya LED lamp controller board with visible electronic components. Electronic module on a black background with an antenna. Control module with components on a blue PCB, visible identifier and connecting traces. PCB module with electronic components on a black surface. WiFi module PCB WK21034 with pin labels.
    The LEDs are 12V, not WS2812B, but SM16703:
    The image shows an open black LED lamp casing with visible LED strips and wiring.
    After connecting, the device flashes in pairing mode:



    You can easily connect to the Tuya application:
    Tuya LED lamp undergoing reverse engineering process.
    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:
    TuyaMCU packet analyzer with hex data.



    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:
    Interface of TuyaMCU Explorer/Analyzer showing decoded data packets.
    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.
    TuyaMCU analysis screen displaying WiFi packets and data.
    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:
    Screenshot of TuyaMCU Explorer/Analyzer tool with hex format data.

    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
    
    


    ADVERTISEMENT


    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):
    Screenshot of TuyaMCU Explorer analyzer

    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):
    TuyaMCU packet analyzer with network data


    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:
    Screenshot of TuyaMCU Explorer tool with packet data.

    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++
    Log in, to see the code

    - 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:
    User interface for controlling the Tuya LED smart lamp.
    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.
    About Author
    p.kaczmarek2
    Moderator Smart Home
    Offline 
    p.kaczmarek2 wrote 14612 posts with rating 12630, helped 655 times. Been with us since 2014 year.
  • ADVERTISEMENT
  • #2 20814318
    Dark Man
    Level 13  
    Posts: 74
    Rate: 20
    Way to go. Well done!
    I will be happy to use the support for directly connected LEDs to the Beken pin in your software.
    I use it similarly in devices with ESP and Tasmota - I have wall switches, in some of them I installed 1 programmable LED and it controls 3 RGB channels separately, for example the color RED informs about the status of the garage, GREEN about the status of the gate and BLUE about the operation of the electric strike of the gate or gate motor.
    Another example of using LED is to show the water temperature in the boiler, which I convert into color in NODERED and send to the switch in the bathroom :)
  • #3 20814335
    p.kaczmarek2
    Moderator Smart Home
    Posts: 14612
    Help: 655
    Rate: 12630
    Great idea with these LEDs, although I think I've already read it somewhere in another of your posts.

    But wait...

    I see a potential technical problem.
    At the moment, we use SPI, or more precisely, the MOSI pin, to directly control LEDs. This is because we need to send data with very precise timings. MOSI on Bekenach is P16:
    How to access the hardware SPI port on CB2S? P16 (MOSI) is soldered to QFN
    It won't work on another GPIO, and P16 is not available on some CB2S modules, so there may be a problem.

    I previously tried to make a simpler driver, based on plain bit-banging, like this:
    PIC18F45K50 as WS2812 LED strip driver (theory+library)
    but I encountered an interesting problem, namely the timings had unexpected errors, apparently due to some mechanism of reading/caching instructions from flash memory. From what I know, this problem would be solved by placing the pixel sending function in RAM, but only certain sections of RAM allow it and this creates further difficulties...
    Screenshot of project code from the ld file for BK7231.
    Image of a code snippet for configuring memory split into areas for vectors and RAM.

    To sum up, for now you can only use individually addressable LEDs with P16, and as for the rest of the pins, maybe soon....
    Helpful post? Buy me a coffee.
📢 Listen (AI):

FAQ

TL;DR: For anyone replacing Tuya cloud control in a BK7231N LED lamp, this method keeps the €49.02 device fully local: “we didn’t flash the MCU at all.” OpenBeken runs on the WiFi module, replays TuyaMCU packets, restores color and effects, and even adds buttons plus a temperature sensor. [#20812818]

Why it matters: It shows a practical way to keep advanced addressable-LED effects while removing cloud dependence and adding new hardware features.

Option WiFi/control path Main MCU reflashed Effects control Extra hardware features
Original Tuya firmware Tuya app + cloud No Yes, stock only No additions shown
OpenBeken on WiFi module 100% local, OBK/MQTT/Home Assistant No Yes, via scripted dpID 25 packets Yes: buttons + MCP9808

Key insight: The breakthrough is not decoding every LED algorithm inside the MCU. You only need to identify dpID 21, 24, 25, and 20, then replay the right TuyaMCU UART states from OpenBeken.

Quick Facts

  • The lamp uses 12 V SM16703 addressable LEDs, not WS2812B, so the reverse-engineering focus shifts to TuyaMCU packet control rather than driving the strip directly from the BK chip. [#20812818]
  • The tested lamp version cost €49.02, and pairing starts by pressing the MUSIC button on the RF remote. [#20812818]
  • OpenBeken was configured with TuyaMCU at 115200 baud and WiFi state 4, which makes the MCU think the device is fully paired. [#20812818]
  • The color payload on dpID 24 uses a 12-character ASCII-hex HSV format: H = 0–360, S = 0–1000, V = 0–1000. [#20812818]
  • The added MCP9808 sensor was polled on an I2C 1-second cycle, and its reading appeared in the OpenBeken panel and Home Assistant discovery flow. [#20812818]

How do I replace the cloud firmware in a BK7231N Tuya lamp with OpenBeken so it works fully locally?

You flash only the WiFi module and leave the LED-control MCU untouched. 1. Open the lamp, identify the BK7231N WiFi module, and capture its TuyaMCU UART traffic. 2. Flash OpenBeken to the WiFi module, start the TuyaMCU driver, set WiFi state 4, and baud rate 115200. 3. Map dpIDs and script buttons or commands that resend the same states for power, color, and effects. After that, the lamp works locally and no longer needs the vendor cloud. [#20812818]

What is TuyaMCU, and how does it split the work between the WiFi module and the main MCU in LED devices?

“TuyaMCU” is a serial control protocol that links a WiFi module to a separate microcontroller, dividing network tasks from device logic. In this lamp, the BK7231N handles WiFi and outside communication, while the main MCU drives the addressable LEDs and receives remote-control input over UART1. That split lets you replace the WiFi firmware with OpenBeken without reflashing the MCU that actually renders effects. [#20812818]

How can I capture and analyze TuyaMCU UART packets to find which dpIDs control power, color, and effects?

You capture UART traffic while changing exactly one function at a time. 1. Start logging packets between the WiFi module and MCU. 2. Perform one action, such as power on, blue color, or brightness change, then save that log. 3. Compare logs in a TuyaMCU analyzer to see which dpIDs change. This method exposed dpID 20 for on/off, dpID 21 for mode, dpID 24 for color, and dpID 25 for animations. [#20812818]

Which Tuya dpIDs were identified for this BK7231N addressable LED lamp, and what does each one do?

Four dpIDs were identified clearly. dpID 20 switches the LEDs on or off. dpID 21 selects operating mode, where 1 is static color and 2 is animation; the author notes 0 would be CW mode. dpID 24 carries color as a Tuya HSV string. dpID 25 carries a variable-length string that encodes the selected effect or effect data for the addressable LED scenes. [#20812818]

Why is brightness encoded inside the Tuya color string on dpID 24 instead of using a separate brightness dpID?

Brightness is encoded inside dpID 24 because this lamp’s Tuya implementation packs H, S, and V together into one HSV payload. The packet captures showed no separate brightness dpID for color mode. When white brightness changed from 0 to 100, the value changed inside the color string while dpID 21 stayed at mode 1. That design is unusual but consistent across the captured color operations. [#20812818]

What is the Tuya 12-character HSV color format on dpID 24, and how do H, S, and V get encoded as ASCII hex?

It is a 12-character string made from three 4-digit ASCII-hex fields: H, S, and V. Hue uses 0 to 360, while saturation and value use 0 to 1000. The thread gives the exact format as sprintf(str, "%04X%04X%04X", iHue, iSat, iVal);. That means pure color and brightness share one payload, because V is the brightness component inside the encoded HSV string. [#20812818]

How do I script OpenBeken buttons to trigger TuyaMCU animations like Curtain, Collision, Rainbow, Pile, Firework, and Chase?

You create HTTP buttons in OpenBeken and bind each one to a script label. The shown setup enables buttons such as Curtain, Collision, Rainbow, Pile, Firework, and Chase with setButtonEnabled, setButtonLabel, and setButtonCommand. Each button calls startScript autoexec.bat do_xxx, and that label sends dpID 21 with value 2, waits 0.1 seconds, then sends the effect payload on dpID 25. No firmware recompilation is required. [#20812818]

What is stored in Tuya dpID 25 for these individually addressable LEDs, and how are animation packets structured?

dpID 25 stores a variable-length string that represents the selected animation and its parameters. The thread does not fully decode the format, but every complex scene, including Curtain, Collision, Rainbow, Firework, and Chase, is sent there. The payload length changes by effect, which matches the packet captures: simple effects use shorter strings, while multi-segment scenes like Collision and Firework use much longer ones with repeated encoded blocks. [#20812818]

How can I add a MCP9808 temperature sensor to an OpenBeken-based lamp and expose it to Home Assistant?

You connect the MCP9808 over I2C and start its driver in OpenBeken. The shown script uses startDriver MCP9808 7 8 1, sets address 0x30, and sets MCP9808_Cycle 1, which polls every 1 second. After that, OpenBeken shows the temperature in its panel, and the reading appears in Home Assistant through the project’s discovery mechanism. That upgrade adds local sensing without changing the lamp’s main MCU. [#20812818]

What’s the best way to add physical buttons in OpenBeken for power, next dimmer level, next color, and effect selection?

The best approach is to wire buttons between free GPIOs and ground, then assign built-in OpenBeken roles. The thread used Button for power, Button_NextDimmer for brightness steps, and Button_NextColor for color changes. For effects, it used a dedicated script-trigger role instead of a hardcoded firmware feature. This method keeps the hardware simple and makes the controls editable from the GPIO configuration page. [#20812818]

How do I use Btn_ScriptOnly, addEventHandler, Choice, and channels in OpenBeken to cycle through LED effects?

You store the current effect index in a channel and advance it on each click. The example uses channel 10, Btn_ScriptOnly, and addEventHandler OnClick 24 backlog addChannel 10 1 0 4 1; do_chosen_effect. Then Choice maps the channel value to commands such as cmd_cur, cmd_col, cmd_rai, cmd_pil, cmd_fir, and cmd_chase. That creates a looping effect selector without recompiling firmware or adding a custom UI page. [#20812818]

SM16703 vs WS2812B — what are the practical differences for reverse engineering and firmware control in LED lamps?

For this lamp, the practical difference is control path, not just LED type. The strip uses 12 V SM16703 LEDs, while the thread explicitly says it is not WS2812B. Because a separate MCU already drives the strip, the reverse-engineering task centers on TuyaMCU dpIDs and UART packets. With directly connected WS2812-style LEDs, you must generate tight signal timing from the BK chip itself, which is a different firmware problem. [#20812818]

Why does OpenBeken currently use SPI MOSI on P16 for directly connected addressable LEDs on Beken chips?

OpenBeken uses SPI MOSI on P16 because those LEDs need very precise timing, and the SPI path provides it reliably. The thread states that direct LED control currently uses MOSI, and on Beken chips that pin is P16. It also warns that this will not work on another GPIO, and some CB2S modules do not expose P16 at all. That hardware limit is the main edge case for direct-drive designs. [#20814335]

What causes timing errors when bit-banging WS2812-style LEDs on BK7231, and why might running code from RAM help?

Timing errors appear because instruction fetch or flash caching disturbs the nanosecond-level pulse timing required by WS2812-style LEDs. The author says a simpler bit-banged driver showed unexpected timing errors, likely from instruction read and cache behavior in flash. Running the pixel-send routine from RAM could reduce that jitter, but only certain RAM sections allow it, which adds implementation difficulty on BK7231 devices. [#20814335]

How can I control OpenBeken TuyaMCU animations from Home Assistant or MQTT when automatic discovery only covers the basic functions?

You control the animations by sending the exact scripted OpenBeken commands over MQTT or from Home Assistant YAML. The thread says automatic discovery covers the basic functions, but effect triggering still must be written manually by sending the proper command. In practice, that means calling the same actions used by the OBK buttons, which set dpID 21 to animation mode and then send the chosen dpID 25 payload. [#20812818]
Generated by the language model.
ADVERTISEMENT