Support of Winsum ZH03B sensor Dust particle sensor in xsns_18_pms5003.ino (#19850)

* Update xsns_18_pms5003.ino

The Winsum ZH03B sensor is quite similar to the Plantower PMS3003 sensor. The main difference is the response in passive mode. With these changes xsns_18_pms5003.ino can support the ZH03B sensor when PMS_MODEL_ZH03X is defined in the configuration file.

* Update xsns_18_pms5003.ino
This commit is contained in:
FransOv 2023-10-27 17:34:49 +02:00 committed by GitHub
parent f45b55a18b
commit 0130ce972a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -27,6 +27,8 @@
* You can either support PMS3003 or PMS5003-7003 at one time. To enable the PMS3003 support
* you must enable the define PMS_MODEL_PMS3003 on your configuration file.
* For PMSx003T models that report temperature and humidity define PMS_MODEL_PMS5003T
* This module can also support de Winsen ZH03x series of dust particle sensors,
* To support those sensors, you must define PMS_MODEL_ZH03X in the confuguration file.
\*********************************************************************************************/
#define XSNS_18 18
@ -61,6 +63,15 @@ enum PmsCommands
CMD_READ_DATA
};
#ifdef PMS_MODEL_ZH03X
const uint8_t kPmsCommands[][9] PROGMEM = {
// 0 1 2 3 4 5 6 7 8
{0xFF, 0x01, 0x78, 0x40, 0x00, 0x00, 0x00, 0x00, 0x47}, // pms_set_active_mode
{0xFF, 0x01, 0xA7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x57}, // pms_sleep
{0xFF, 0x01, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58}, // pms_wake
{0xFF, 0x01, 0x78, 0x41, 0x00, 0x00, 0x00, 0x00, 0x46}, // pms_set_passive_mode
{0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}}; // pms_passive_mode_read
#else
const uint8_t kPmsCommands[][7] PROGMEM = {
// 0 1 2 3 4 5 6
{0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71}, // pms_set_active_mode
@ -68,12 +79,13 @@ const uint8_t kPmsCommands[][7] PROGMEM = {
{0x42, 0x4D, 0xE4, 0x00, 0x01, 0x01, 0x74}, // pms_wake
{0x42, 0x4D, 0xE1, 0x00, 0x00, 0x01, 0x70}, // pms_set_passive_mode
{0x42, 0x4D, 0xE2, 0x00, 0x00, 0x01, 0x71}}; // pms_passive_mode_read
#endif // PMS_MODEL_ZH03X
struct pmsX003data {
uint16_t framelen;
uint16_t pm10_standard, pm25_standard, pm100_standard;
uint16_t pm10_env, pm25_env, pm100_env;
#ifdef PMS_MODEL_PMS3003
#if defined(PMS_MODEL_PMS3003) || defined(PMS_MODEL_ZH03X)
uint16_t reserved1, reserved2, reserved3;
#else
uint16_t particles_03um, particles_05um, particles_10um, particles_25um;
@ -104,7 +116,7 @@ bool PmsReadData(void)
while ((PmsSerial->peek() != 0x42) && PmsSerial->available()) {
PmsSerial->read();
}
#ifdef PMS_MODEL_PMS3003
#if defined(PMS_MODEL_PMS3003) || defined(PMS_MODEL_ZH03X)
if (PmsSerial->available() < 24) {
#else
if (PmsSerial->available() < 32) {
@ -112,7 +124,7 @@ bool PmsReadData(void)
return false;
}
#ifdef PMS_MODEL_PMS3003
#if defined(PMS_MODEL_PMS3003) || defined(PMS_MODEL_ZH03X)
uint8_t buffer[24];
PmsSerial->readBytes(buffer, 24);
#else
@ -122,14 +134,14 @@ bool PmsReadData(void)
uint16_t sum = 0;
PmsSerial->flush(); // Make room for another burst
#ifdef PMS_MODEL_PMS3003
#if defined(PMS_MODEL_PMS3003) || defined(PMS_MODEL_ZH03X)
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 24);
#else
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 32);
#endif // PMS_MODEL_PMS3003
// get checksum ready
#ifdef PMS_MODEL_PMS3003
#if defined(PMS_MODEL_PMS3003) || defined(PMS_MODEL_ZH03X)
for (uint32_t i = 0; i < 22; i++) {
#else
for (uint32_t i = 0; i < 30; i++) {
@ -137,7 +149,7 @@ bool PmsReadData(void)
sum += buffer[i];
}
// The data comes in endian'd, this solves it so it works on all platforms
#ifdef PMS_MODEL_PMS3003
#if defined(PMS_MODEL_PMS3003) || defined(PMS_MODEL_ZH03X)
uint16_t buffer_u16[12];
for (uint32_t i = 0; i < 12; i++) {
#else
@ -147,7 +159,8 @@ bool PmsReadData(void)
buffer_u16[i] = buffer[2 + i*2 + 1];
buffer_u16[i] += (buffer[2 + i*2] << 8);
}
#ifdef PMS_MODEL_PMS3003
#if defined(PMS_MODEL_PMS3003) || defined(PMS_MODEL_ZH03X)
if (sum != buffer_u16[10]) {
#else
if (sum != buffer_u16[14]) {
@ -156,7 +169,7 @@ bool PmsReadData(void)
return false;
}
#ifdef PMS_MODEL_PMS3003
#if defined(PMS_MODEL_PMS3003) || defined(PMS_MODEL_ZH03X)
memcpy((void *)&pms_data, (void *)buffer_u16, 22);
#else
memcpy((void *)&pms_data, (void *)buffer_u16, 30);
@ -170,6 +183,60 @@ bool PmsReadData(void)
return true;
}
#ifdef PMS_MODEL_ZH03X
bool ZH03ReadDataPassive() // process the passive mode response of the ZH03x sensor
{
if (! PmsSerial->available()) {
return false;
}
while ((PmsSerial->peek() != 0xFF) && PmsSerial->available()) {
PmsSerial->read();
}
if (PmsSerial->available() < 9) {
return false;
}
uint8_t buffer[9];
PmsSerial->readBytes(buffer, 9);
if (buffer[1] != 0x86) {
return false;
}
PmsSerial->flush(); // Make room for another burst
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 9);
uint8_t sum = 0;
for (uint32_t i = 1; i < 7; i++) {
sum += buffer[i];
}
sum=(~sum)+1;
if (sum != buffer[8]) {
AddLog(LOG_LEVEL_DEBUG, PSTR("ZH03x: " D_CHECKSUM_FAILURE));
return false;
}
uint16_t buffer_u16[12];
for (uint32_t i = 1; i < 4; i++) {
buffer_u16[i] = buffer[i*2 + 1];
buffer_u16[i] += (buffer[i*2] << 8);
buffer_u16[i+3] = buffer[i*2 + 1]; // Direct and Environment values identical
buffer_u16[i+3] += (buffer[i*2] << 8); // Direct and Environment values identical
buffer_u16[0] = 20; // set dummy framelength
buffer_u16[11] = buffer[8]; // copy checksum
}
memcpy((void *)&pms_data, (void *)buffer_u16, 22);
Pms.valid = 10;
if (!Pms.discovery_triggered) {
TasmotaGlobal.discovery_counter = 1; // Force discovery
Pms.discovery_triggered = true;
}
return true;
}
#endif // PMS_MODEL_ZH03X
/*********************************************************************************************\
* Command Sensor18
@ -227,7 +294,18 @@ void PmsSecond(void) // Every second
}
if (Pms.ready) {
#ifdef PMS_MODEL_ZH03X
bool validread;
if (Settings->pms_wake_interval >= MIN_INTERVAL_PERIOD) {
validread = ZH03ReadDataPassive(); // in passive mode, the response is different from the PMS sensors
}
else {
validread = PmsReadData(); // In active mode the rsponse is identical to the PMS sensors
}
if (validread) {
#else
if (PmsReadData()) {
#endif // PMS_MODEL_ZH03X
Pms.valid = 10;
if (Settings->pms_wake_interval >= MIN_INTERVAL_PERIOD) {
PmsSendCmd(CMD_SLEEP);
@ -276,7 +354,10 @@ void PmsInit(void) {
void PmsShow(bool json) {
if (Pms.valid) {
char types[10];
#ifdef PMS_MODEL_PMS3003
#ifdef PMS_MODEL_ZH03X
strcpy_P(types, PSTR("ZH03x"));
#elif defined(PMS_MODEL_PMS3003)
strcpy_P(types, PSTR("PMS3003"));
#elif defined(PMS_MODEL_PMS5003T)
strcpy_P(types, PSTR("PMS5003T"));
@ -294,7 +375,7 @@ void PmsShow(bool json) {
types,
pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard,
pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env);
#ifndef PMS_MODEL_PMS3003
#if !(defined(PMS_MODEL_PMS3003) || defined(PMS_MODEL_ZH03X))
ResponseAppend_P(PSTR(",\"PB0.3\":%d,\"PB0.5\":%d,\"PB1\":%d,\"PB2.5\":%d,"),
pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um);
#ifdef PMS_MODEL_PMS5003T
@ -321,7 +402,7 @@ void PmsShow(bool json) {
WSContentSend_PD(HTTP_SNS_ENVIRONMENTAL_CONCENTRATION, types, "1", pms_data.pm10_env);
WSContentSend_PD(HTTP_SNS_ENVIRONMENTAL_CONCENTRATION, types, "2.5", pms_data.pm25_env);
WSContentSend_PD(HTTP_SNS_ENVIRONMENTAL_CONCENTRATION, types, "10", pms_data.pm100_env);
#ifndef PMS_MODEL_PMS3003
#if !(defined(PMS_MODEL_PMS3003) || defined(PMS_MODEL_ZH03X))
WSContentSend_PD(HTTP_SNS_PARTICALS_BEYOND, types, "0.3", pms_data.particles_03um);
WSContentSend_PD(HTTP_SNS_PARTICALS_BEYOND, types, "0.5", pms_data.particles_05um);
WSContentSend_PD(HTTP_SNS_PARTICALS_BEYOND, types, "1", pms_data.particles_10um);