logo elektroda
logo elektroda
X
logo elektroda

Hoping for XR1 Module. Generic Tuya Window/Door Sensor with CB3S

divadiow 1872 15
ADVERTISEMENT
  • Helpful post
    #1 21182477
    divadiow
    Level 38  
    Posts: 4839
    Help: 420
    Rate: 852
    I've found this site quite useful for putting all reviewer-posted Ali Express product pictures in one easy to browse location: https://alitools.io/en/showcase
    Sometimes rare modules like XR1 and TW-03 can be spotted.
    Photos of buyers of the Tuya LED controller.

    For example, this LED controller looks like it might have (or once came with) a TW-03 module.

    https://alitools.io/en/showcase/tuya-smart-li...-work-with-alexa-echo-google-1005001402683965

    LED controller circuit board with visible Wi-Fi module labeled TW-03.

    The subject of this post is a generic Tuya battery-powered magnetic window/door sensor that I saw with an XR1 (XR809) module in one picture. Mine came with a CB3S however.
    No matter, it can still be opened, analysed and flashed to OpenBeken.

    Tuya window-door sensor with CB3S module in a plastic housing without batteries Tuya magnetic window and door sensor with white casing. Tuya sensor PCB with visible electronic components. Image of the interior of a plastic sensor casing, with a visible battery compartment. Image of a printed circuit board with a Tuya CB3S module. CB3S module mounted on a blue PCB with connected wires on a wooden surface.

    App screen displaying an open WiFi door sensor. App settings screen showing battery level and alarm options. Device update screen showing no updates available. Main module and MCU module versions are V1.0.10. Closing reminder from the WiFi door sensor. Record history of WiFi door sensor opening and closing.

    Tuya door-window magnetic sensor with packaging and manual on a table. Wi-Fi door sensor packaging with text and QR codes.

    The age of the build date and fw version means it is cloudcutterable https://github.com/tuya-cloudcutter/tuya-clou...es/tuya-generic-cb3s-door-sensor-v1.0.10.json

    though the pin assignments differ for this variant.

    Code: Text
    Log in, to see the code


    Code: JSON
    Log in, to see the code


    UART boot log out

    Code: Text
    Log in, to see the code


    OBK template
    Code: JSON
    Log in, to see the code


    and what seems to be a reasonable autoexec:

    Code: Text
    Log in, to see the code

    DSEdge 0 is tested to wake the device if magnet pulls away (door/window opens) from the sensor when it is in deepsleep. Conversely if the sensor is in deepsleep when the magnet returns to the sensor (door/window closes), the device also wakes from deepsleep.

    OpenBK software interface for a Tuya sensor.

    https://github.com/OpenBekenIOT/webapp/pull/140
    Attachments:
    • readResult_BK7231N_QIO_2024-05-8-21-41-14.bin (2 MB) You must be logged in to download this attachment.
  • ADVERTISEMENT
  • #2 21182777
    p.kaczmarek2
    Moderator Smart Home
    Posts: 14394
    Help: 650
    Rate: 12315
    This seems like a very good and useful site, thank you for sharing!
    Tuya really seems to change the modules often, so no wonder it was using TW2 once...

    By the way, this device seems to be using the battery driver. Are you aware about this problem where Bat_Relay channel can't be used for other purposes? Maybe we could try to fix it? Just ... detach battery driver from channels?
    Helpful post? Buy me a coffee.
  • ADVERTISEMENT
  • #3 21182788
    divadiow
    Level 38  
    Posts: 4839
    Help: 420
    Rate: 852
    p.kaczmarek2 wrote:
    This seems like a very good and useful site, thank you for sharing!

    sure. It's definitely a gamble buying based on the pics. User pics are often for different products or it's unclear which product was chosen on a multi-option page.

    p.kaczmarek2 wrote:
    Are you aware about this problem where Bat_Relay channel can't be used for other purposes? Maybe we could try to fix it? Just ... detach battery driver from channels?


    Well, I knew/assumed the BAT_Relay channel needed to not be used by any other assignment, but my BAT_Relay channel 3 is not assigned anything else...

    What does deatching the battery driver from channels entail?
  • #4 21182794
    p.kaczmarek2
    Moderator Smart Home
    Posts: 14394
    Help: 650
    Rate: 12315
    I will try to change drv_battery.c code so it just sets a pin, not a whole channel:
    Screenshot of C code with a snippet about setting a channel for drv_battery.
    Would you be able to test that?
    Helpful post? Buy me a coffee.
  • ADVERTISEMENT
  • #5 21182796
    divadiow
    Level 38  
    Posts: 4839
    Help: 420
    Rate: 852
    of course
  • #6 21182887
    p.kaczmarek2
    Moderator Smart Home
    Posts: 14394
    Help: 650
    Rate: 12315
    Here is a proposed solution, but I am not able to test it:
    https://github.com/openshwprojects/OpenBK7231T_App/pull/1303/files
    This should write directly to first found Bat_Relay or Bat_Relay_n pin instead of writing to whole channel.
    Helpful post? Buy me a coffee.
  • #7 21182997
    divadiow
    Level 38  
    Posts: 4839
    Help: 420
    Rate: 852
    not sure I understand the original issue, but anyway

    Screenshot of the OpenBK_BK7231N_CB3S_DoorWindow user interface displaying device status information.

    Screenshot showing an interface with a dropdown menu and a text box with the value 3 entered.
  • #8 21182998
    insmod
    Level 31  
    Posts: 1353
    Help: 160
    Rate: 425
    >>21182887 Does this fix a bug, where if bat_relay is on, then *_AppendInformationToHTTPIndexPage doesn't work?
  • #9 21184466
    p.kaczmarek2
    Moderator Smart Home
    Posts: 14394
    Help: 650
    Rate: 12315
    So battery driver still works good after my PR?

    insmod wrote:
    >>21182887 Does this fix a bug, where if bat_relay is on, then *_AppendInformationToHTTPIndexPage doesn't work?

    I don't know anything about that bug, why would even that happen? How to reproduce?
    Helpful post? Buy me a coffee.
  • #10 21184571
    divadiow
    Level 38  
    Posts: 4839
    Help: 420
    Rate: 852
    p.kaczmarek2 wrote:
    So battery driver still works good after my PR?

    Yes. Seems to be
  • #11 21184576
    insmod
    Level 31  
    Posts: 1353
    Help: 160
    Rate: 425
    >>21184466
    One of my devices (CBU+CHT8310) have, what i believe, is a bad adc.
    I power it with a single 18650 battery.
    Graph of battery voltage changes in mV showing fluctuations.
    So i tailored myself a patch, which takes multiple measures, and then publishes a median one.
    Though it is not enough, it still is better than what was before.
    So with this patch, adc takes, in my case, 20 measurements with 25ms delay between them. So for half a second bat_relay is on.
    If i update the main page during the measurements, the bug happens.
    I occasionally ran into this even without the patch, and didn't understand what is the reason.
    
    commit b39ae29e8faa59b1ad604750a67c534f049a44f4
    Author: NonPIayerCharacter <18557343+NonPIayerCharacter@users.noreply.github.com>
    Date:   Fri May 31 14:47:19 2024 +0100
    
        --
    
    diff --git a/src/driver/drv_battery.c b/src/driver/drv_battery.c
    index 0cfb93d9cbbdbd377917a744350002ad0f37a358..7081b3bc23b7b20ecc8241a27542ee82a5a1333d 100644
    --- a/src/driver/drv_battery.c
    +++ b/src/driver/drv_battery.c
    @@ -12,21 +12,31 @@
     #include "../hal/hal_adc.h"
     #include "drv_battery.h"
     
    -static int g_pin_adc = 0, channel_adc = 0, channel_rel = 0, g_pin_rel = 0, g_battcycle = 1, g_battcycleref = 10;
    +static int g_pin_adc = 0, channel_adc = 0, channel_rel = 0, g_pin_rel = 0, g_battcycle = 1, g_battcycleref = 10, g_oversamp = 1, g_sampdelay = 100;
     static float g_battvoltage = 0.0, g_battlevel = 0.0;
     static int g_lastbattvoltage = 0, g_lastbattlevel = 0;
     static float g_vref = 2400, g_vdivider = 2.29, g_maxbatt = 3000, g_minbatt = 2000, g_adcbits = 4096;
    +static bool g_isoversamp = false;
    +
    +int cmp_adc(const void* a, const void* b)
    +{
    +   int arg1 = *(const int*)a;
    +   int arg2 = *(const int*)b;
    +
    +   return (arg1 > arg2) - (arg1 < arg2);
    +}
     
     static void Batt_Measure() {
        //this command has only been tested on CBU
        float batt_ref, batt_res, vref;
        ADDLOG_INFO(LOG_FEATURE_DRV, "DRV_BATTERY : Measure Battery volt en perc");
        g_pin_adc = PIN_FindPinIndexForRole(IOR_BAT_ADC, g_pin_adc);
    +   bool hasRelay = true;
        if (PIN_FindPinIndexForRole(IOR_BAT_Relay, -1) == -1 && PIN_FindPinIndexForRole(IOR_BAT_Relay_n, -1) == -1) {
    -      g_vdivider = 1;
    +      hasRelay = false;
        }
        // if divider equal to 1 then no need for relay activation
    -   if (g_vdivider > 1) {
    +   if (g_vdivider > 1 && hasRelay) {
           g_pin_rel = PIN_FindPinIndexForRole(IOR_BAT_Relay, -1);
           if (g_pin_rel == -1) {
              g_pin_rel = PIN_FindPinIndexForRole(IOR_BAT_Relay_n, -1);
    @@ -38,13 +48,32 @@ static void Batt_Measure() {
        if (g_battlevel < 1024) {
           ADDLOG_INFO(LOG_FEATURE_DRV, "DRV_BATTERY : ADC Value low device not on battery");
        }
    -   if (g_vdivider > 1) {
    +   if(g_vdivider > 1 && hasRelay)
    +   {
           CHANNEL_Set(channel_rel, 1, 0);
           rtos_delay_milliseconds(10);
        }
    -   g_battvoltage = HAL_ADC_Read(g_pin_adc);
    +   if(g_isoversamp)
    +   {
    +      g_battvoltage = 0;
    +      int oversamp = g_oversamp > 0 ? g_oversamp : 2;
    +      int vals[oversamp];
    +      for(int i = 0; i < oversamp; i++)
    +      {
    +         vals[i] = HAL_ADC_Read(g_pin_adc);
    +         ADDLOG_EXTRADEBUG(LOG_FEATURE_DRV, "DRV_BATTERY: adc %i", vals[i]);
    +         rtos_delay_milliseconds(g_sampdelay);
    +      }
    +      qsort(vals, oversamp, sizeof(int), cmp_adc);
    +      g_battvoltage = vals[oversamp / 2];
    +   }
    +   else
    +   {
    +      g_battvoltage = HAL_ADC_Read(g_pin_adc);
    +   }
        ADDLOG_DEBUG(LOG_FEATURE_DRV, "DRV_BATTERY : ADC binary Measurement : %f and channel %i", g_battvoltage, channel_adc);
    -   if (g_vdivider > 1) {
    +   if (g_vdivider > 1 && hasRelay)
    +   {
           CHANNEL_Set(channel_rel, 0, 0);
        }
        ADDLOG_DEBUG(LOG_FEATURE_DRV, "DRV_BATTERY : Calculation with param : %f %f %f", g_vref, g_adcbits, g_vdivider);
    @@ -123,13 +152,37 @@ commandResult_t Battery_cycle(const void* context, const char* cmd, const char*
        {
           return CMD_RES_NOT_ENOUGH_ARGUMENTS;
        }
    -   g_battcycleref = Tokenizer_GetArgFloat(0);
    +   g_battcycleref = Tokenizer_GetArgInteger(0);
     
        ADDLOG_INFO(LOG_FEATURE_CMD, "Battery Cycle : Measurement will run every %i seconds", g_battcycleref);
     
        return CMD_RES_OK;
     }
     
    +commandResult_t Battery_Sampling(const void* context, const char* cmd, const char* args, int cmdFlags)
    +{
    +   Tokenizer_TokenizeString(args, TOKENIZER_ALLOW_QUOTES | TOKENIZER_DONT_EXPAND);
    +   if(Tokenizer_CheckArgsCountAndPrintWarning(cmd, 2))
    +   {
    +      return CMD_RES_NOT_ENOUGH_ARGUMENTS;
    +   }
    +   g_oversamp = Tokenizer_GetArgInteger(0);
    +   g_sampdelay = Tokenizer_GetArgInteger(1);
    +   g_sampdelay = g_sampdelay < 10 ? 10 : g_sampdelay;
    +
    +   if((g_oversamp * g_sampdelay + 100) > g_battcycleref * 1000)
    +   {
    +      ADDLOG_WARN(LOG_FEATURE_CMD, "Battery_Sampling: New cycle will begin before old one is completed, disabling oversampling!");
    +      g_isoversamp = false;
    +   }
    +   else
    +   {
    +      g_isoversamp = true;
    +      ADDLOG_INFO(LOG_FEATURE_CMD, "Battery_Sampling: Oversampling %i times with delay %ims between sampling", g_oversamp, g_sampdelay);
    +   }
    +
    +   return CMD_RES_OK;
    +}
     
     // startDriver Battery
     void Batt_Init() {
    @@ -146,6 +199,11 @@ void Batt_Init() {
        //cmddetail:"examples":"Battery_cycle 60"}
        CMD_RegisterCommand("Battery_cycle", Battery_cycle, NULL);
     
    +   //cmddetail:{"name":"Battery_Sampling","args":"[samples][delay]",
    +   //cmddetail:"descr":"Oversample readings default is off. args: samples, delay between cycles is ms",
    +   //cmddetail:"fn":"Battery_Sampling","file":"drv/drv_battery.c","requires":"",
    +   //cmddetail:"examples":"Battery_Sampling 10 100"}
    +   CMD_RegisterCommand("Battery_Sampling", Battery_Sampling, NULL);
     }
     
     void Batt_OnEverySecond() {
    
    
  • #13 21788359
    p.kaczmarek2
    Moderator Smart Home
    Posts: 14394
    Help: 650
    Rate: 12315
    Thanks for sharing. Do you have flash backup?
    This long part is not a fuse, it's a sensor.
    You have slightly different device type, you have external one.
    White Tuya WiFi door/window sensor with magnet and status light
    Helpful post? Buy me a coffee.
  • #14 21788403
    cijoml
    Level 9  
    Posts: 74
    Rate: 3
    Code: JSON
    Log in, to see the code


    Device configuration, as extracted from Tuya:
    - Button (channel 0) on P20
    - Status LED on P26
    - PIR sensor on P22
    - Battery Relay on P14
    - Battery Max Voltage: 3000
    - Battery Min Voltage: 1800
    - Battery ADC on P23
    Device seems to use Battery Driver. See more details here: https://www.elektroda.com/rtvforum/topic3959103.html
    Device seems to be using CB3S module, which is using BK7231N.
    And the Tuya section starts, as usual, at 2023424
    Attachments:
    • readResult_BK7231N_QIO_ws_2025-23-12-13-53-08.bin (2 MB) You must be logged in to download this attachment.
  • ADVERTISEMENT
  • #15 21788421
    cijoml
    Level 9  
    Posts: 74
    Rate: 3
    What is the best autoexec.bat for this device? I flashed it right now. Seems sensor return values

Topic summary

✨ The discussion revolves around a generic Tuya battery-powered magnetic window/door sensor, which the author found equipped with a CB3S module instead of the expected XR1 (XR809) module. Users share insights on the variability of modules in Tuya products and the challenges of purchasing based on user-uploaded images. A technical issue regarding the BAT_Relay channel's functionality is raised, with suggestions to modify the drv_battery.c code to allow the battery driver to operate independently of other channel assignments. Proposed solutions and patches are discussed, including a GitHub pull request aimed at addressing the issue where the BAT_Relay being active interferes with other functionalities. The conversation also touches on ADC measurement issues in another device, leading to the development of a patch for improved performance.
Generated by the language model.

FAQ

TL;DR: Tuya CB3S/BK7231N door sensors are hackable, use a 4096‑step ADC, and “This should write directly to first found Bat_Relay…”. Use the provided OpenBeken template, DSEdge 0 for wake, and optional Cloudcutter. [Elektroda, p.kaczmarek2, post #21182887]

Why it matters: This FAQ helps DIYers flash, configure, and troubleshoot Tuya door/window sensors for local, battery‑savvy automation without cloud lock‑in.

Quick facts:

Quick Facts

What device is this FAQ for, and who should use it?

A generic Tuya magnetic window/door sensor seen with XR1 in photos but delivered with CB3S (BK7231N). It’s for makers wanting OpenBeken, MQTT, and long battery life without Tuya cloud. [Elektroda, divadiow, post #21182477]

Is this exact door sensor Cloudcutter‑compatible?

Yes. Based on firmware age and version, it’s flagged as cloudcutterable; the post links the specific profile and shows UART logs confirming Tuya SDK 2.3.1. [Elektroda, divadiow, post #21182477]

How do I map the pins in OpenBeken?

Use the provided OBK template: P14=BAT_Relay;3, P20=Btn;0, P22=dInput_NoPullUp;2, P23=BAT_ADC;1, P26=WifiLED_n;0. Apply the template and image link from the post. [Elektroda, divadiow, post #21182477]

How can I set deep sleep and still wake on door open/close?

Set DSEdge 0. The author verified it wakes when the magnet leaves or returns during deep sleep. The example script sleeps 60 s then 86,400 s for daily heartbeat. [Elektroda, divadiow, post #21182477]

What’s the benefit of the battery‑driver PR mentioned here?

It changes the driver to write directly to the first Bat_Relay/Bat_Relay_n pin, avoiding channel conflicts. “This should write directly to first found Bat_Relay...” is how the author described it. [Elektroda, p.kaczmarek2, post #21182887]

Did someone test the new battery‑driver behavior?

Yes. A tester replied that the battery driver still worked after the PR. That confirms functional parity while enabling freer channel use. [Elektroda, divadiow, post #21184571]

I saw a bug when Bat_Relay is on and I refresh the main page. Is there a fix?

One user reproduced hangs during long Bat_Relay on‑time with ADC oversampling (20 reads × 25 ms). They added median sampling and a guard to disable oversampling if a cycle overlaps. Reduce relay on‑time or sampling window. [Elektroda, insmod, post #21184576]

How do I identify whether my unit uses XR1 or CB3S?

Check user photos on listings and open the shell if needed. The thread shows XR1 sightings in photos but the unit received carried a CB3S module labeled BK7231N. [Elektroda, divadiow, post #21182477]

What is Tuya in this context?

Tuya is the OEM cloud firmware shown by the UART log string “TUYA IOT SDK V:2.3.1,” including product and firmware keys. You’ll replace its behavior with OpenBeken. [Elektroda, divadiow, post #21182477]

What is OpenBeken?

OpenBeken is community firmware for BK7231‑based devices. Here it’s used with templates, scripts, and a webapp PR link to customize sensor behavior and power. [Elektroda, divadiow, post #21182477]

Can I spot compatible modules before buying?

Yes. The thread recommends using AliTools’ consolidated reviewer photos to find rare modules like XR1 or TW‑03 on multi‑option listings. It’s still a gamble. [Elektroda, divadiow, post #21182477]

Do I need to reserve the Bat_Relay channel for the battery driver?

With the PR approach, the driver toggles the relay pin directly, not the whole channel. That reduces conflicts with other channel uses. [Elektroda, p.kaczmarek2, post #21182887]

What ADC resolution and voltage range does the script assume?

The driver uses 4096 steps (12‑bit) and configures 1.8–3.0 V as Min/Max. Battery_Setup sets Vref and divider for proper percentage reporting. [Elektroda, divadiow, post #21182477]

Where did someone buy the exact same device recently?

A user reported receiving the same device from an AliExpress listing on December 23, 2025, and shared the product URL. [Elektroda, cijoml, post #21788321]

How do I flash and configure this sensor with OpenBeken? (3 steps)

  1. Confirm it’s the CB3S/BK7231N variant; verify Tuya SDK string via UART or photos. 2. Use the linked Cloudcutter profile if applicable, then install OpenBeken. 3. Apply the OBK template, paste the autoexec with DSEdge 0, MQTT, and sleep timings. [Elektroda, divadiow, post #21182477]

Any edge cases I should watch for?

Refreshing the HTTP index during prolonged Bat_Relay activation plus ADC oversampling may cause issues. Keep measurement windows short or add an overlap guard as shown. [Elektroda, insmod, post #21184576]
Generated by the language model.
ADVERTISEMENT