mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-23 18:56:38 +00:00
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:
parent
f45b55a18b
commit
0130ce972a
@ -27,6 +27,8 @@
|
|||||||
* You can either support PMS3003 or PMS5003-7003 at one time. To enable the PMS3003 support
|
* 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.
|
* you must enable the define PMS_MODEL_PMS3003 on your configuration file.
|
||||||
* For PMSx003T models that report temperature and humidity define PMS_MODEL_PMS5003T
|
* 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
|
#define XSNS_18 18
|
||||||
@ -61,6 +63,15 @@ enum PmsCommands
|
|||||||
CMD_READ_DATA
|
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 = {
|
const uint8_t kPmsCommands[][7] PROGMEM = {
|
||||||
// 0 1 2 3 4 5 6
|
// 0 1 2 3 4 5 6
|
||||||
{0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71}, // pms_set_active_mode
|
{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, 0xE4, 0x00, 0x01, 0x01, 0x74}, // pms_wake
|
||||||
{0x42, 0x4D, 0xE1, 0x00, 0x00, 0x01, 0x70}, // pms_set_passive_mode
|
{0x42, 0x4D, 0xE1, 0x00, 0x00, 0x01, 0x70}, // pms_set_passive_mode
|
||||||
{0x42, 0x4D, 0xE2, 0x00, 0x00, 0x01, 0x71}}; // pms_passive_mode_read
|
{0x42, 0x4D, 0xE2, 0x00, 0x00, 0x01, 0x71}}; // pms_passive_mode_read
|
||||||
|
#endif // PMS_MODEL_ZH03X
|
||||||
|
|
||||||
struct pmsX003data {
|
struct pmsX003data {
|
||||||
uint16_t framelen;
|
uint16_t framelen;
|
||||||
uint16_t pm10_standard, pm25_standard, pm100_standard;
|
uint16_t pm10_standard, pm25_standard, pm100_standard;
|
||||||
uint16_t pm10_env, pm25_env, pm100_env;
|
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;
|
uint16_t reserved1, reserved2, reserved3;
|
||||||
#else
|
#else
|
||||||
uint16_t particles_03um, particles_05um, particles_10um, particles_25um;
|
uint16_t particles_03um, particles_05um, particles_10um, particles_25um;
|
||||||
@ -104,7 +116,7 @@ bool PmsReadData(void)
|
|||||||
while ((PmsSerial->peek() != 0x42) && PmsSerial->available()) {
|
while ((PmsSerial->peek() != 0x42) && PmsSerial->available()) {
|
||||||
PmsSerial->read();
|
PmsSerial->read();
|
||||||
}
|
}
|
||||||
#ifdef PMS_MODEL_PMS3003
|
#if defined(PMS_MODEL_PMS3003) || defined(PMS_MODEL_ZH03X)
|
||||||
if (PmsSerial->available() < 24) {
|
if (PmsSerial->available() < 24) {
|
||||||
#else
|
#else
|
||||||
if (PmsSerial->available() < 32) {
|
if (PmsSerial->available() < 32) {
|
||||||
@ -112,7 +124,7 @@ bool PmsReadData(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef PMS_MODEL_PMS3003
|
#if defined(PMS_MODEL_PMS3003) || defined(PMS_MODEL_ZH03X)
|
||||||
uint8_t buffer[24];
|
uint8_t buffer[24];
|
||||||
PmsSerial->readBytes(buffer, 24);
|
PmsSerial->readBytes(buffer, 24);
|
||||||
#else
|
#else
|
||||||
@ -122,14 +134,14 @@ bool PmsReadData(void)
|
|||||||
uint16_t sum = 0;
|
uint16_t sum = 0;
|
||||||
PmsSerial->flush(); // Make room for another burst
|
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);
|
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 24);
|
||||||
#else
|
#else
|
||||||
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 32);
|
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 32);
|
||||||
#endif // PMS_MODEL_PMS3003
|
#endif // PMS_MODEL_PMS3003
|
||||||
|
|
||||||
// get checksum ready
|
// get checksum ready
|
||||||
#ifdef PMS_MODEL_PMS3003
|
#if defined(PMS_MODEL_PMS3003) || defined(PMS_MODEL_ZH03X)
|
||||||
for (uint32_t i = 0; i < 22; i++) {
|
for (uint32_t i = 0; i < 22; i++) {
|
||||||
#else
|
#else
|
||||||
for (uint32_t i = 0; i < 30; i++) {
|
for (uint32_t i = 0; i < 30; i++) {
|
||||||
@ -137,7 +149,7 @@ bool PmsReadData(void)
|
|||||||
sum += buffer[i];
|
sum += buffer[i];
|
||||||
}
|
}
|
||||||
// The data comes in endian'd, this solves it so it works on all platforms
|
// 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];
|
uint16_t buffer_u16[12];
|
||||||
for (uint32_t i = 0; i < 12; i++) {
|
for (uint32_t i = 0; i < 12; i++) {
|
||||||
#else
|
#else
|
||||||
@ -147,7 +159,8 @@ bool PmsReadData(void)
|
|||||||
buffer_u16[i] = buffer[2 + i*2 + 1];
|
buffer_u16[i] = buffer[2 + i*2 + 1];
|
||||||
buffer_u16[i] += (buffer[2 + i*2] << 8);
|
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]) {
|
if (sum != buffer_u16[10]) {
|
||||||
#else
|
#else
|
||||||
if (sum != buffer_u16[14]) {
|
if (sum != buffer_u16[14]) {
|
||||||
@ -156,7 +169,7 @@ bool PmsReadData(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef PMS_MODEL_PMS3003
|
#if defined(PMS_MODEL_PMS3003) || defined(PMS_MODEL_ZH03X)
|
||||||
memcpy((void *)&pms_data, (void *)buffer_u16, 22);
|
memcpy((void *)&pms_data, (void *)buffer_u16, 22);
|
||||||
#else
|
#else
|
||||||
memcpy((void *)&pms_data, (void *)buffer_u16, 30);
|
memcpy((void *)&pms_data, (void *)buffer_u16, 30);
|
||||||
@ -170,6 +183,60 @@ bool PmsReadData(void)
|
|||||||
|
|
||||||
return true;
|
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
|
* Command Sensor18
|
||||||
@ -227,7 +294,18 @@ void PmsSecond(void) // Every second
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Pms.ready) {
|
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()) {
|
if (PmsReadData()) {
|
||||||
|
#endif // PMS_MODEL_ZH03X
|
||||||
Pms.valid = 10;
|
Pms.valid = 10;
|
||||||
if (Settings->pms_wake_interval >= MIN_INTERVAL_PERIOD) {
|
if (Settings->pms_wake_interval >= MIN_INTERVAL_PERIOD) {
|
||||||
PmsSendCmd(CMD_SLEEP);
|
PmsSendCmd(CMD_SLEEP);
|
||||||
@ -276,7 +354,10 @@ void PmsInit(void) {
|
|||||||
void PmsShow(bool json) {
|
void PmsShow(bool json) {
|
||||||
if (Pms.valid) {
|
if (Pms.valid) {
|
||||||
char types[10];
|
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"));
|
strcpy_P(types, PSTR("PMS3003"));
|
||||||
#elif defined(PMS_MODEL_PMS5003T)
|
#elif defined(PMS_MODEL_PMS5003T)
|
||||||
strcpy_P(types, PSTR("PMS5003T"));
|
strcpy_P(types, PSTR("PMS5003T"));
|
||||||
@ -294,7 +375,7 @@ void PmsShow(bool json) {
|
|||||||
types,
|
types,
|
||||||
pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard,
|
pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard,
|
||||||
pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env);
|
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,"),
|
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);
|
pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um);
|
||||||
#ifdef PMS_MODEL_PMS5003T
|
#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, "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, "2.5", pms_data.pm25_env);
|
||||||
WSContentSend_PD(HTTP_SNS_ENVIRONMENTAL_CONCENTRATION, types, "10", pms_data.pm100_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.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, "0.5", pms_data.particles_05um);
|
||||||
WSContentSend_PD(HTTP_SNS_PARTICALS_BEYOND, types, "1", pms_data.particles_10um);
|
WSContentSend_PD(HTTP_SNS_PARTICALS_BEYOND, types, "1", pms_data.particles_10um);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user