Skip to content

Instantly share code, notes, and snippets.

Created November 1, 2020 09:38
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save cretep/f96606dc6a4eae0d85993d6085959220 to your computer and use it in GitHub Desktop.
Winsen ZE08-CH2O (Formaldehyde sensor) for ESPHome
- winsen_ze08.h
- id: uart_ze08
rx_pin: GPIO16
tx_pin: GPIO17
baud_rate: 9600
- lambda: |-
auto ze08 = new WinsenZE08Sensor(id(uart_ze08), id(ze08_ch2o));
return {ze08};
- platform: template
name: Formaldehyde
id: ze08_ch2o
unit_of_measurement: ppb
accuracy_decimals: 0
#include "esphome.h"
// Pinouts on page 4 of datasheet:
static const char *TAG = "custom";
static const uint8_t ZE08_SET_QA_MODE[] = {0xFF, 0x01, 0x78, 0x41, 0x00, 0x00, 0x00, 0x00, 0x46};
//static const uint8_t ZE08_SET_ACTIVE_MODE[] = {0xFF, 0x01, 0x78, 0x40, 0x00, 0x00, 0x00, 0x00, 0x47};
static const uint8_t ZE08_QUESTION[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
static const char *ZE08_MODE_QA = "QA";
//static const char *ZE08_MODE_ACTIVE = "ACTIVE";
static const char *ZE08_MODE = ZE08_MODE_QA;
class WinsenZE08Sensor : public PollingComponent, public UARTDevice {
Sensor *ch2o_ppb_ {nullptr};
WinsenZE08Sensor(UARTComponent *parent, Sensor *ch2o_ppb) : UARTDevice(parent), ch2o_ppb_(ch2o_ppb) {}
void setup() override {
if (ZE08_MODE == ZE08_MODE_QA) {
write_array(ZE08_SET_QA_MODE, sizeof(ZE08_SET_QA_MODE));
void loop() override {
void update() override {
write_array(ZE08_QUESTION, sizeof(ZE08_QUESTION));
unsigned char buf[9];
if (this->available() != sizeof(buf)) {
ESP_LOGE(TAG, "Bad response from ZE08! received %d bytes.", this->available());
readBytes(buf, sizeof(buf));
// message of 9 bytes, same reading two units:
// ug/m3 ppb
// st cm high low res res high low ch
unsigned short concentration_ugm3 = (buf[2] << 8) | buf[3];
unsigned short concentration_ppb = (buf[6] << 8) | buf[7];
ESP_LOGD(TAG, "Received %d %d.", concentration_ugm3, concentration_ppb);
unsigned char checksum_calc = ~(buf[1] + buf[2] + buf[3] + buf[4] + buf[5] + buf[6] + buf[7]) + 1;
if (buf[8] != checksum_calc) {
ESP_LOGE(TAG, "Bad checksum from ZE08! received %d != %d.", buf[8], checksum_calc);
Copy link

lwh201314 commented Dec 3, 2021

Esphome compilation error

In file included from src/main.cpp:28:0:
src/winsen_ze08.h: In member function 'virtual void WinsenZE08Sensor::update()':
src/winsen_ze08.h:41:33: error: 'readBytes' was not declared in this scope
readBytes(buf, sizeof(buf));
*** [.pioenvs/stream-server/src/main.cpp.o] Error 1

Copy link

haplm commented Feb 17, 2022

Having the same problem, it seems that something changed in the ESPHome interfaces (or in the SDK). Would it be possible to tak a look, please?

Copy link

marianri commented Mar 2, 2022

You can replace:
readBytes(buf, sizeof(buf));
read_array(buf, sizeof(buf));

Copy link

haplm commented Mar 2, 2022

Perfect, thank you, that worked!

Copy link

The sensor I have seems to overestimate readings - 25 ppb even outside. Don't know if there is a way to correct it. If any one can share experience with ze08-ch2o .

Copy link

Hello. Could I use your library (winsen_ze08.h) for ZE25?
Снимок экрана 2024-02-05 в 12 21 53

Copy link

cretep commented Feb 5, 2024

Hello. Could I use your library (winsen_ze08.h) for ZE25?

Yes, and to re-licence any parts under GNU GPL as necessary.

(I'm not actively using this sensor at present so cannot provide further details/support.)

Copy link

I didn't ask about the license...
I looked at the datasheet for the ZE25-O3 and it looks like your code can be completely used.
I don't understand programming.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment