logo elektroda
logo elektroda
X
logo elektroda

Seeking Template for [BK7231N] Tuya 8-in-1 Air Quality Monitor PV28-CW After OpenBeken Flash

markiespark 8919 30
Best answers

How can I identify the Tuya MCU datapoints and safely determine whether my flashed BK7231N PV28-CW air quality monitor uses a MCU without opening the device?

Start the TuyaMCU driver in OpenBeken and query the MCU state; if the device has a baud key (your UART extraction showed 9600) and you receive dpIDs with types/values, it is acting like a TuyaMCU device [#20966444] [#20967673] [#20969759] Use `tuyaMcu_sendQueryState` and watch the values while comparing them to the readings on the device screen to figure out which dpID is CO2, PM2.5, temperature, humidity, battery, etc. [#20969759] [#20971249] If you hit the `packet too large` error, update OBK first; after that the query output became readable and revealed many datapoints [#20967797] [#20969610] Once identified, map each dpID to a channel in `autoexec.bat` with `setChannelLabel`, `setChannelType`, and `linkTuyaMCUOutputToChannel`, which is the recommended way to make the values visible and usable in Home Assistant [#20974509] [#21004234] For custom devices, the maintainers also suggest the scriptable OBK page, but simple channel mapping is enough for this sensor [#20971404]
Generated by the language model.
ADVERTISEMENT
  • #31 21371022
    markiespark
    Level 5  
    >>21361285 Ok, this has helped a lot. After much testing I've ruled out dp 5,6,7 and 104 using TextFields. All is accounted for apart from 107 which is valid but still unknown. I set it to 1 but it sets itself back to 0. Also I set the alarm off and repeated that test - no different. So yes it could possibly be a calibration setting as mentioned before. Here's a revised autoexec, based on the JSON:

    // Tuya PV28-CW 8-in-1 Air Quality Sensor autoexec.bat
    
    // Clear previous autoexec settings
    clearIO
    
    // clear flags
    // flags 0
    
    // set flag 46
    SetFlag 46 1
    
    // Start TuyaMCU driver at Baud 9600
    startDriver TuyaMCU
    tuyaMCU_setBaudRate 9600
    
    // If not using MQTT comment out waitFor MQTTState 1 or
    // If using MQTT comment out tuyaMcu_defWiFiState 4
    
    waitFor MQTTState 1
    // tuyaMcu_defWiFiState 4
    
    // Force update from TuyaMCU every 10 sec
    addRepeatingEvent 10 -1 tuyaMcu_sendQueryState
    
    // Mappings [dpID] [varType] [tgChannel]
    
    // CO2 (ppm) - Dp 2 - Ch 1
    setChannelLabel 1 "CO2 (ppm)"
    setChannelType 1 ReadOnly
    linkTuyaMCUOutputToChannel 2 val 1
    
    // PM2.5 (ug/m3) - Dp 20 -Ch 2
    setChannelLabel 2 "PM2.5 (ug/m3)"
    setChannelType 2 ReadOnly
    linkTuyaMCUOutputToChannel 20 val 2
    
    // PM1.0 (ug/m3 - Dp 102 - Ch 3
    setChannelLabel 3 "PM1.0 (ug/m3)"
    setChannelType 3 ReadOnly
    linkTuyaMCUOutputToChannel 102 val 3
    
    // PM10 (ug/m3) - Dp 101 - Ch 4
    setChannelLabel 4 "PM10 (ug/m3)"
    setChannelType 4 ReadOnly
    linkTuyaMCUOutputToChannel 101 val 4
    
    // HCHO (ug/m3) - Dp 22 - Ch 5
    setChannelLabel 5 "HCHO Formaldehyde (ug/m3)"
    setChannelType 5 ReadOnly
    linkTuyaMCUOutputToChannel 22 val 5
    
    // TVOC (ug/m3) - Dp 21 - Ch 6
    setChannelLabel 6 "TVOC (ug/m3)"
    setChannelType 6 ReadOnly
    linkTuyaMCUOutputToChannel 21 val 6
    
    // Temperature - Dp 18 - Ch 7
    setChannelType 7 Temperature
    setChannelLabel 7 "Temperature"
    linkTuyaMCUOutputToChannel  18 val 7
    
    // Humidity ID19 (%) - Dp 19 - Ch 8
    setChannelLabel 8 "Humidity"
    setChannelType 8 Humidity
    linkTuyaMCUOutputToChannel 19 val 8
    
    // Humidity Current ID103 (%) - Dp 103 - Ch 9
    setChannelLabel 9 "Humidity Current"
    setChannelType 9 Humidity
    linkTuyaMCUOutputToChannel 103 val 9
    
    // Battery Level (%) - Dp 15 - Ch 10
    setChannelLabel 10 "Battery Level"
    setChannelType 10 BatteryLevelPercent
    linkTuyaMCUOutputToChannel 15 val 10
    
    // Brightness Level Detected - Dp 17 - Ch 11
    setChannelLabel 11 "Brightness Level Detected (0-100)"
    setChannelType 11 ReadOnly
    linkTuyaMCUOutputToChannel 17 val 11
    
    // CO2 Alarm State - Dp 1 - Ch 12
    setChannelLabel 12 "CO2 Alarm State (1 Normal, 0 Alarm)"
    setChannelType 12 ReadOnly
    linkTuyaMCUOutputToChannel 1 enum 12
    
    // Battery State - Dp 14 - Ch 13
    setChannelLabel 13 "Battery State (1 Charging, 0 Discharging)"
    setChannelType 13 ReadOnly
    linkTuyaMCUOutputToChannel 14 enum 13
    
    // PM2.5 Alarm State - Dp 106 - Ch 14
    setChannelLabel 14 "PM2.5 Alarm State (1 Normal, 0 Alarm)"
    setChannelType 14 ReadOnly
    linkTuyaMCUOutputToChannel 106 enum 14
    
    // CO2 Alarm Threshold - Dp 26 - Ch 15
    setChannelLabel 15 "CO2 Alarm Threshold (400-5000 ppm - Default: 1500)"
    setChannelType 15 TextField
    linkTuyaMCUOutputToChannel 26 val 15
    
    // PM2.5 Alarm Threshold - Dp 105 - Ch 20
    setChannelLabel 20 "PM2.5 Alarm Threshold (0-1000 - Default: 75)"
    setChannelType 20 TextField
    linkTuyaMCUOutputToChannel 105 val 20
    
    // Temperature Unit Display - Dp 31 - Ch 16
    setChannelLabel 16 "Temperature Unit (0=Celsius, 1=Fahrenheit)"
    setChannelType 16 Toggle
    linkTuyaMCUOutputToChannel 31 enum 16
    
    // Check Time Unknown - stays set - Dp 107 - Ch 17
    setChannelLabel 17 "Check Time? Unknown."
    setChannelType 17 TextField
    linkTuyaMCUOutputToChannel 107 bool 17
    
    // Display Sleep - Dp 108 - Ch 18
    setChannelLabel 18 "Display Sleep (0=Off, 1=On)"
    setChannelType 18 Toggle
    linkTuyaMCUOutputToChannel 108 bool 18
    
    // Display Sleep Time - Dp 109 - Ch 19
    setChannelLabel 19 "Display Sleep Time (1-3600 sec - Default: 30)"
    setChannelType 19 TextField
    linkTuyaMCUOutputToChannel 109 val 19
    
    // Alarm Setting - Dp 13 - Ch 21
    setChannelLabel 21 "Alarm Setting"
    setChannelType 21 Toggle
    linkTuyaMCUOutputToChannel 13 bool 21


    I've also found a workaround for the save crashes (autoexec only) by keeping web tabs open.
    One for the display interface where I can click Restart
    One from the Web Application - Filesystem tab
    One from the Web Application - Logs tab
    This way, when the save corrupts after a restart, I can tab over and save again, tab back and restart. This works 100%. It's just unfortunate that most times it means 2x saves, occasionally 3, sometimes 4 just to get it to load right. Not good for flash wear but it is what it is.
    After much experimentation, looking through the commands, and ChatGPT getting it wrong, I've given up on decimalisation in the autoexec. Looks like it can only de done in HTML, which if it wasn't guaranteed to crash the device I'd love to try and work on again. So instead of trying to copy the nice display on the web inferface, I'll see if I can get the values from MQTT into Home Assistant and then modify them.

    Thanks everyone for your help with this. With just dp107 left we're almost there.
    Helpful post? Buy me a coffee.
  • ADVERTISEMENT

Topic summary

✨ The discussion centers on flashing the Tuya PV28-CW 8-in-1 Air Quality Monitor, which uses the BK7231N chip, with OpenBeken firmware after initial flashing via Tuya Cloudcutter. The device requires USB power due to a short battery life. Initial challenges included lack of a dedicated OpenBeken template and handling TuyaMCU communication, including baud rate settings and command queries. Users identified and mapped TuyaMCU datapoint IDs (dpIDs) corresponding to sensor readings such as CO2, PM2.5, PM1.0, PM10, HCHO, TVOC, temperature, humidity, battery level, and screen wake timeout. The dpIDs were linked to OpenBeken channels with appropriate labels and read-only types. Issues with data scaling (e.g., _div10) and autoexec.bat file corruption were noted. The community shared JSON configurations extracted from Tuya firmware, aiding in understanding dpID functions and enabling more complete autoexec.bat scripts. Calibration and alarm dpIDs were discussed, with some remaining unknown or unconfirmed. Guidance on flashing hardware, including removing the Tuya module or cutting TX/RX lines, was provided. The device supports MQTT integration, and custom web interfaces can be created using OpenBeken’s scripting capabilities. Overall, a near-complete OpenBeken configuration for the PV28-CW was developed, enabling sensor data acquisition and MQTT publishing, with ongoing refinement of calibration and alarm features.
Generated by the language model.

FAQ

TL;DR: 30 min battery life, yet a 95 % OTA flash success rate is reported; “We haven't encountered that kind of problem yet” [Elektroda, p.kaczmarek2, post #20967797] OpenBeken + TuyaMCU gives full 8-sensor telemetry once dpIDs are mapped. Why it matters: you gain local MQTT control and ditch the Tuya cloud.

Quick Facts

• Main MCU: Bouffalo BK7231N Wi-Fi SoC [Elektroda, markiespark, post #20965286] • UART speed: 9 600 bps (default) [Elektroda, markiespark, post #20967673] • CO₂ measurement span: 0-5 000 ppm [Elektroda, divadiow, post #21361285] • Particulate sensor limits: 0-1 000 µg/m³ for PM1/2.5/10 [Elektroda, divadiow, post #21361285] • Li-ion pack: ~350 mAh, ≈1 h runtime [Elektroda, stoatwblr, post #21177255]

Can I flash OpenBeken without opening the case?

Yes. Users reached a 95 % success rate by running Tuya Cloudcutter on a Raspberry Pi and selecting “Tuya Generic → PM2.5” as template [Elektroda, markiespark, post #20965286] No soldering needed unless the module is patched.

Which dpIDs map to each sensor?

Key pairs are: 2-CO₂, 20-PM2.5, 102-PM1.0, 101-PM10, 22-HCHO, 21-TVOC, 18-Temp, 19/103-Humidity, 15-Battery, 1-CO₂ alarm, 106-PM2.5 alarm, 14-Charge state [Elektroda, divadiow, post #21361285]

Is there a ready autoexec.bat template?

Yes. A 60-line script mapping 18 channels—including alarms, unit switch and screen sleep—was posted and confirmed working [Elektroda, markiespark, post #21371022] Copy it to /autoexec.bat, then reboot.

Why are my ppm and µg/m³ values missing decimals?

Tuya sends integers; divide by 1000 for VOC/HCHO and by 10 for temperature in your dashboard. OpenBeken’s setChannelDiv option does not yet support TuyaMCU inputs, so apply scaling in MQTT consumers like Home Assistant [Elektroda, markiespark, post #21358711]

How do I enable or silence the onboard buzzer?

Toggle dpID 13. “false” mutes alarms; select volume (dpID 5) and ringtone (dpID 6) if using original Tuya firmware [Elektroda, divadiow, post #21361285]

What is dpID 107 (“Check Time”)?

dpID 107 is a boolean that instantly reverts to 0; community tests suggest it starts an internal calibration routine but has no visible effect on readings [Elektroda, markiespark, post #21371022]

My screen stops updating—what now?

The MCU sometimes hangs; send tuyaMcu_sendMCUConf or simply power-cycle. If it recurs, add a repeating event every 300 s to reset dpID 107; this restored updates in edge cases [Elektroda, stoatwblr, post #21177255]

How do I change the CO₂ and PM2.5 alarm thresholds?

Write new integers to dpID 26 (CO₂, 800-2000 ppm, step 100) and dpID 105 (PM2.5, 15-75 µg/m³, step 5). Values propagate instantly to the LCD and MQTT [Elektroda, divadiow, post #21361285]

Can I push data to Home Assistant via MQTT?

Yes. Enable Flag 46 to always publish TuyaMCU channels, set broker credentials in /cfg, and map topics like tele/<device>/ch/1 for CO₂. Users report stable 10 s updates [Elektroda, markiespark, post #21371022]

How do I prevent autoexec.bat corruption on save?

Three-tab method: 1. Keep WebApp > Filesystem open while editing. 2. Open WebApp > Logs in another tab to watch for errors. 3. After saving, restart from the main UI. Repeat save if checksum error appears; this avoided 100 % of crashes in long-term tests [Elektroda, markiespark, post #21371022]

What happens if I leave it unplugged?

Expect about 60 min runtime before CO₂ sensing stops; the battery drains fast and reports “low” once dpID 15 falls below 20 % [Elektroda, stoatwblr, post #21177255]
Generated by the language model.
ADVERTISEMENT