Elektroda.com
Elektroda.com
X

WXDM2 dual dimmer - reverse engineering - strange UART protocol

p.kaczmarek2 2034 6
This content has been translated flag-pl » flag-en View the original version here.
  • WXDM2 dual dimmer - reverse engineering - strange UART protocol
    I will present here an analysis of the interior and a short reverse engineering of the UART protocol of another Tuya dimmer, this time based on a rather unusual LMB54 (BK7231N) module and two HC32F003 microcontrollers. I will fully describe here how it can be programmed and added its OpenBeken support using the scripting language offered by these firmwares. The presented dimmer will be quite unusual, because it does not use the TuyaMCU protocol as discussed products before , only from a slightly different, simpler UART communication, which in this case is unidirectional.


    Purchase WXDM2
    The dimmer gave me a reader (along with a set of other gadgets) to upload OpenBeken to them, as I wrote in the previous part. The purchase of the dimmer itself took place on the Polish auction site:
    WXDM2 dual dimmer - reverse engineering - strange UART protocol
    The dimmer cost PLN 80, which is quite a lot, but at least it supports two light bulbs and two switches.
    Description from seller:
    WXDM2 dual dimmer - reverse engineering - strange UART protocol
    Important note - in the original firmware it dims by pressing the button, so the monostable button is preferred:
    WXDM2 dual dimmer - reverse engineering - strange UART protocol
    assembly diagram:
    WXDM2 dual dimmer - reverse engineering - strange UART protocol
    But beware, the product shipped has the N and L places swapped, you should follow the markings on its housing, they have the "last word"


    Received set
    This seller doesn't even provide mounting screws or tape in the kit...
    WXDM2 dual dimmer - reverse engineering - strange UART protocol WXDM2 dual dimmer - reverse engineering - strange UART protocol WXDM2 dual dimmer - reverse engineering - strange UART protocol WXDM2 dual dimmer - reverse engineering - strange UART protocol WXDM2 dual dimmer - reverse engineering - strange UART protocol
    WXDM2 dual dimmer - reverse engineering - strange UART protocol

    WXDM2 interior, module pins and PCB analysis
    Just pry the cover:
    WXDM2 dual dimmer - reverse engineering - strange UART protocol WXDM2 dual dimmer - reverse engineering - strange UART protocol
    The parts of the dimmer I analyzed. Here you can see a 3.3V LDO regulator, an optocoupler, a MOSFET (7N65C), 8 S3M diodes and a PN8016 converter controller.
    WXDM2 dual dimmer - reverse engineering - strange UART protocol
    WXDM2 dual dimmer - reverse engineering - strange UART protocol
    We look further - the WiFi module is not signed, it is not WB3S:
    WXDM2 dual dimmer - reverse engineering - strange UART protocol
    Let's desolder the upper PCB:
    WXDM2 dual dimmer - reverse engineering - strange UART protocolWXDM2 dual dimmer - reverse engineering - strange UART protocol WXDM2 dual dimmer - reverse engineering - strange UART protocol
    Here is the second MOSFET, 7N65C... and a large capacitor, but I haven't analyzed its role.
    On the bottom of the PCB with the WiFi module, there are up to two HC32F003 microcontrollers - could it be a product from TuyaMCU? But how is it two? One per dimming channel?
    WXDM2 dual dimmer - reverse engineering - strange UART protocol
    WXDM2 dual dimmer - reverse engineering - strange UART protocol
    Now you need to unsolder the screen from the WiFi module to see what's inside.
    WXDM2 dual dimmer - reverse engineering - strange UART protocol
    BK7231N! There are chances to change the firmware. But where is the RX and TX?
    Let's desolder the WiFi module to check its pinout description.
    Hot air is essential here.
    WXDM2 dual dimmer - reverse engineering - strange UART protocol WXDM2 dual dimmer - reverse engineering - strange UART protocol
    WXDM2 dual dimmer - reverse engineering - strange UART protocol

    WXDM2 dual dimmer - reverse engineering - strange UART protocol WXDM2 dual dimmer - reverse engineering - strange UART protocol
    Finally, I developed a sketch:
    WXDM2 dual dimmer - reverse engineering - strange UART protocol
    Explains:
    - 3.3V and GND is known, power supply
    - S1 and S2 are button inputs, they are connected to the WiFi module
    - RX and TX from the WiFi module is led outside
    - TX from WiFi module goes to both HC32F003
    - R1 and R2 are dimmer outputs, each supports one HC32F003
    - ZC is zero detection so that the dimmer knows when to start conducting (it is connected through a transistor, to both HC)
    I also tried eavesdropping on UART communication. Only on the TX from the WiFi system something is happening.
    WXDM2 dual dimmer - reverse engineering - strange UART protocol
    Collected data as text:
    
    FF 55 01 3F 59 00 00 0A
    FF 55 02 3F 3F 00 00 0A
    FF 55 01 59 3F 00 00 0A 
    FF 55 02 59 59 00 00 0A
    FF 55 01 3F 59 00 00 0A
    FF 55 02 3F 3F 00 00 0A 
    FF 55 01 59 3F 00 00 0A
    FF 55 02 59 59 00 00 0A        
    
    
    
    00
    FF 55 01 FF 00 05 DC 0A
    0A FF 55 02 FF FF 05 DC 0A
    
    00 FF 55 01 FF 00 05 DC 0A FF 55 02 FF FF 05 DC 0A                         
     FF 
    55 01 FF 00 05 DC 0A FF 55 02 FF FF 05 DC 0A    
    

    Byte 0x55 looks like TuyaMCU protocol, but TuyaMCU is not.
    0x01 and 0x02 look like identifiers which dimmer we are setting.
    I decided to upload OpenBeken and check if I am able to send the UART packet controlling the dimmer myself.

    Uploading the firmware and the first tests of control
    However, it quickly turned out that all the fun with the soldering was unnecessary - in fact, only the WiFI module "talks" to the UART here, so it can be programmed in the system. Nothing will disturb us in the MCU. I marked the pads in the previous paragraph. Here is the connection of the programmer:
    WXDM2 dual dimmer - reverse engineering - strange UART protocol WXDM2 dual dimmer - reverse engineering - strange UART protocol
    I uploaded the batch via:
    https://github.com/openshwprojects/BK7231GUIFlashTool
    according to the instructions on the page above.
    then in OpenBeken I used the uartSendHex command to test sending various packets to the dimmers. I wanted to simulate the operation of the original firmware. This is necessary to control these dimmers.
    After some testing, I've come to the conclusion that the following package works:
    
    uartSendHex 0A FF 55 02 00 9F 00 00 0A
    

    And 9F is the brightness level value, from 0 to 255, one byte. There is no checksum here. I don't know what the rest of the bytes are, except for 0x02 which is the ID of which of the two dimmers on board we are controlling.

    Proof of concept - test script
    I already have a suspicion of how it works. So I can prepare a simple OpenBeken script that changes the brightness of the lamp in a loop. Will the script work as expected?
    
    again:
    addChannel 10 1 0 255 1
    delay_ms 100
    uartSendHex 0A FF 55 02 00 $CH10$ 00   00 0A
    
    goto again
    

    The script above operates on OpenBeken channel 10. It gradually increases it, after reaching 255 it loops from 0. The value of channel 10 is used as the byte value in the sent packet.
    OBK commands documentation:
    https://github.com/openshwprojects/OpenBK7231T_App/blob/main/docs/commands.md
    The script works:




    The final example - the bar and mapping its changes to the UART value
    OpenBeken offers the ability to set the type of channels, i.e. the way they are displayed. The Dimmer256 type is a dimmer strip with a range of 0 to 255, so it's as found here. In combination with the script below, we can get the dimmer control on the web panel:
    
    addEventHandler OnChannelChange 0 uartSendHex 0A FF 55 02 00 $CH0$ 00   00 0A
    

    When channel 0 is changed, a given message will be sent to the UART. This is enough to have real control over this dimmer..
    Result:
    WXDM2 dual dimmer - reverse engineering - strange UART protocol





    Summary
    It was quite an unusual product. I have not met a dimmer from Tuya before, but without TuyaMCU, using instead a simplified, "one-way" UART protocol (i.e. here only the WiFi module sends information, it does not receive anything). This "unidirectionality" is a bit justified for me, however, because there are separate MCUs inside to dim each channel. Both are connected to one TX1 line from BK7231N and they only select packets intended for them by their content (one of the bytes is 0x01 or 0x02). The dimmer is already working in OpenBeken, although it could be improved a bit and, for example, remembering the dimmer state (at the moment there is only one variable, so turning off the lamp is equivalent to setting the dimmer to 0). All this is possible to do by OBK scripts, so I consider the dimmer to be anyway supported and in the future I will give a separate topic about the already mentioned scripts.

    Cool? Ranking DIY
    About Author
    p.kaczmarek2
    Moderator Smart Home
    Offline 
    p.kaczmarek2 wrote 5843 posts with rating 5827, helped 279 times. Been with us since 2014 year.
  • #3
    pedropaislopes
    Level 3  
    Hi! This is my first post, so please be easy with me...

    I have a similar module. I've purchased it on an online store in my country (Brazil) for around 20 US dollars. It uses Tuya smart APP and has support for two dimmer circuits: L1 and L2. They can be controlled independently on the APP and on an external wall push button (S1 and S2). S1 and S2 should be connected on AC voltage, what is a great deal specially if the dimmer module is installed on the ceiling, for example.

    Here are some photos.

    WXDM2 dual dimmer - reverse engineering - strange UART protocol

    It opens easily on the back side.

    WXDM2 dual dimmer - reverse engineering - strange UART protocol

    Here are the pins of the CB3S controller that are soldered on the back green board.

    WXDM2 dual dimmer - reverse engineering - strange UART protocol

    So it has a TX only communication to (another) controlled or chip, that exists on the back of this little green board.

    WXDM2 dual dimmer - reverse engineering - strange UART protocol

    On the back side the controller is a HK32F030MF4P6.

    The main difference, in my point of view, is that this module uses only one ARM chip to control the dimmer. The Original Post (OP) module uses two chips, one for each dimmer circuit.

    After some desoldering and sniffing on the UART protocol I've came with the same findings that the OP get. Sending on TX pin, at a baud rate of 9600 8n1, specially coded hex strings changes the dimmer. The rule is:

    - 0a ff 55 01_or_02 L1brightness L2brightness Dimmer_ramp Dimmer_ramp 0a

    Explained a little more, byte-per-byte:

    - 0a ff 55: start of message. Can't find a explanation (TuyaMCU related?)
    - 01_or_02: sending 01 changes L1 brightness. Sending 02 changes L2 brightness.
    - L1brightness: the dimmer brightness of L1. 00 is 0 brightness (off, on my understand), FF is 255 brightness (maximum brightness)
    - L2brightness: the same of L1brightness
    - Dimmer_ramp (two bytes): original message of my module is "05 dc", that presents a smooth brightness change (around half a second). Changing both to 00 produces an immediate change of brightness. On my tests I can't change L1 and L2 smooth brightness change independently.
    - 0a: last byte

    After flashing OpenBEKEN on the CB3S, soldering back things, was time to configure it.

    The CB3S pins are as described on the following picture.

    WXDM2 dual dimmer - reverse engineering - strange UART protocol

    But I've asked: can OpenBEKEN be configured to enable dimmer change with S1 and S2 pins? Of course!

    Here is my autoexec.bat with comments explained easy line (only for L1, as L2 is very similar):

    
    // Enabling and setting ntp driver
    startDriver ntp
    ntp_setServer 200.160.0.8
    ntp_timeZoneOfs -3:00
    // TuyaMCU driver that permits sending hex coding on TX pin to communicate with dimmer controller
    startDriver TuyaMCU
    // Clearing any handler that was used on debugging
    clearAllHandlers
    // Starts with both dimmers at 0 brightness (off state)
    uartSendHex 0A FF 55 01 00 00 05 DC 0A
    uartSendHex 0A FF 55 02 00 00 05 DC 0A
    //
    //
    // L1 channels and configurations
    // Dimmer256 has 0 to 255 range
    setChannelType 11 dimmer256
    // Channel 21 will act as a "variable"...
    setChannelType 21 TextField
    // ... initialized with 1.
    setChannel 21 1
    // Channel 21 represents the long press of S1. If S1 is long pressed the brightness will change 15 points. It will increase
    // brightness if channel 21 is 1, and decrease brightness if channel 21 is -1
    // So this way I could mimic the original Tuya firmware behavior.
    //
    // A label for channel 11, that represents L1 brightness state
    SetChannelLabel 11 Espelho_Sala 0
    // Clamping channel 11 to its values. I think that this command is redundant...
    ClampChannel 11 0 255
    // Channel 11 starts with 0, reflecting L1 state (see first uartSendHex above)
    setChannel 11 0
    //
    // Events with S1 (Botton switch that links to L1)
    // If a fast click is captured on PIN 8 (S1 module AC connector), and channel 11 is 0 (off), set its brightness (channel value) to 255 (max)
    // if not, set to 0 (off)
    // BUT, at the same time, changes channel 21 value. If dimmer is switched on, long press on S1 will (of course) decrease brightness.
    // If dimmer is switched off, long press on S1 will (of course) increase.
    addEventHandler OnClick 8 if $CH11==0 then "backlog setChannel 11 255; setChannel 21 -1" else "backlog setChannel 11 0; setChannel 21 1"
    // The OnHold event. Observe that the conditional is based do $CH21.
    addEventHandler OnHold 8 if $CH21==1 then "addChannel 11 15 0 255" else "addChannel 11 -15 0 255"
    // And, after release of a OnHold (long press) event, changes the direction of the next long press.
    addEventHandler OnRelease 8 if $CH21==1 then "setChannel 21 -1" else "setChannel 21 1"
    // Finally sends the hex string to the dimmer controller, based on Channel 11 value
    addEventHandler OnChannelChange 11 uartSendHex 0A FF 55 01 $CH11$ 00 05 DC 0A
    
    // channel 12 links to module L2
    setChannelType 12 dimmer256
    setChannelType 22 TextField
    setChannel 22 1
    SetChannelLabel 12 Espelho_Sala 0
    ClampChannel 12 0 255
    setChannel 12 0
    addEventHandler OnClick 9 if $CH12==0 then "backlog setChannel 12 255; setChannel 22 -1" else "backlog setChannel 12 0; setChannel 22 1"
    addEventHandler OnHold 9 if $CH22==1 then "addChannel 12 15 0 255" else "addChannel 12 -15 0 255"
    addEventHandler OnRelease 9 if $CH22==1 then "setChannel 22 -1" else "setChannel 22 1"
    addEventHandler OnChannelChange 12 uartSendHex 0A FF 55 02 00 $CH12$ 05 DC 0A
    


    Amazing what OpenBEKEN can do!!!!!

    Now the home assistant part, that will communicate with the dimmer module by MQTT. On OpenBEKEN the MQTT Client Topic is DM02.

    
    mqtt:
      light:
        - name: "Espelho Sala"
          # Defines the HASS entity
          unique_id: dm02_espelho_sala
          schema: template
          # Defines light entity online/offline based on the whole module state
          availability_topic: "DM02/connected"
          # State definition. 11 is the channel of L1
          state_topic: "DM02/11/get"
          state_template: '{{ "off" if value | int == 0 else "on" }}'
          # Command to switch on/off
          command_topic: "DM02/11/set"
          # This "brightness" is a variable that reflects the click on the HASS web/app interface light slide
          # But the button bellow this slide should turn ON the light, too. Turn on on maximum brightness.
          command_on_template: "{%- if brightness is defined -%}
            {{ brightness }}
            {%- else -%}
            {{ 255 }}
            {%- endif -%}"
          # Zero brightness is off, as OpenBEKEN configuration
          command_off_template: "0"
          # Brightness value (that comes from the "get" topic)
          brightness_template: "{{value}}"
          # Details of MQTT
          qos: 1
          retain: true
          # We infer state based on brightness so optimistic can be disabled
          optimistic: false
    


    And it works! I am absolutely admired how powerful and simple OpenBEKEN is!

    I have some other ideas. I plan to use arithmetic operations with channel 21 (or 22, in case of L2 dimmer) to take out some conditionals on Events, and put on another channel (lets say, channel 31 and 32) the brightness step value for each dimmer, so an user can change it's step on the web interface. And put some names on those auxiliary channels. If I implement these ideas I could send the new autoexec.bat.

    I hope to help someone with this explanation that has a similar dimmer module, the same way that the Original Post helped me.

    And, changing this last dimmer module to OpenBEKEN, the Tuya integration is officially switched off on my Home Assistant! No more cloud dependency!

    Cheers!
  • #4
    p.kaczmarek2
    Moderator Smart Home
    Thank you for a deeper insight into this device. I was not aware about the smooth transition ramp data included in the UART packets.

    The 0a ff 55 seems to me just like a header "magic" constant, which is similar to one in TuyaMCU. Maybe it's just that Tuya likes to use that constant to mark the beginning of their data packets.

    I am surprised that 0a at the end is not a checksum. If I were to design that protocol, I'd included a simple checksum in the packet data.

    Anyway, good job! Thank you for a detailed addition to my work.
  • #5
    ferbulous
    Level 16  
    Hi, how do you press the button on momentary switch if you need to dim or increase the brightness?
    Does each long press just switch to dimming and the reverse?
  • #6
    p.kaczmarek2
    Moderator Smart Home
    Hello @ferbulous , it's been a long time since I've handled that device (I was doing it for our reader) and I no longer have access to it, I'll forward your question, but as far as I remember, the button is momentary and holding it adjusts the brightness.

    Are you interested in buying that particular device, considering that now OBK can support it?
  • #7
    ferbulous
    Level 16  
    Yes, I just don't quite understand how to use it with momentary switch.

    p.kaczmarek2 wrote:
    the button is momentary and holding it adjusts the brightness.

    Indeed, but which action increase or decrease the brightness if you recall

    I have the usual smart dimmer with knobs to adjust the brightness by rotating clockwise or counterclockwise.