From acc314df738f3d1252d4f4e6e8a990c5a1d11d3a Mon Sep 17 00:00:00 2001 From: Yonsm Date: Thu, 26 Apr 2018 14:30:48 +0800 Subject: [PATCH 1/5] Refine PMSX003 to support PMS5003T/ST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Support PMS5003T/PMS5003ST; 2. Replace data reading method; 3. Remove PMS library dependency; 4. Support smart sleeping mode to extend the sensor’s lifetime. --- code/espurna/sensors/PMSX003Sensor.h | 244 +++++++++++++++++++++++---- 1 file changed, 213 insertions(+), 31 deletions(-) diff --git a/code/espurna/sensors/PMSX003Sensor.h b/code/espurna/sensors/PMSX003Sensor.h index 95e5933a..12d26868 100644 --- a/code/espurna/sensors/PMSX003Sensor.h +++ b/code/espurna/sensors/PMSX003Sensor.h @@ -14,7 +14,158 @@ #include #include -class PMSX003Sensor : public BaseSensor { +// +#define PMS_TYPE_X003 0 +#define PMS_TYPE_X003_9 1 +#define PMS_TYPE_5003T 2 +#define PMS_TYPE_5003ST 3 + +#ifndef PMS_TYPE +#define PMS_TYPE PMS_TYPE_X003 +#endif + +// You can enable smart sleep (read 6-times then sleep on 24-reading-cycles) to extend PMS sensor's life. +// Otherwise the default lifetime of PMS sensor is about 8000-hours/1-years. +// The PMS's fan will stop working on sleeping cycle, and will wake up on reading cycle. +#ifndef PMS_SMART_SLEEP +#define PMS_SMART_SLEEP 0 +#endif + +// [MAGIC][LEN][DATA9|13|17][SUM] +#if PMS_TYPE == PMS_TYPE_5003ST +#define PMS_TYPE_NAME "PMS5003ST" +#define PMS_DATA_COUNT 17 +#define PMS_SLOT_COUNT 4 +#define PMS_SLOT_NAMES {"PM2.5", "TEMP", "HUMI", "HCHO"} +#define PMS_SLOT_TYPES {MAGNITUDE_PM2dot5, MAGNITUDE_TEMPERATURE, MAGNITUDE_HUMIDITY, MAGNITUDE_ANALOG} +#elif PMS_TYPE == PMS_TYPE_5003T +#define PMS_TYPE_NAME "PMS5003T" +#define PMS_DATA_COUNT 13 +#define PMS_SLOT_COUNT 3 +#define PMS_SLOT_NAMES {"PM2.5", "TEMP", "HUMI"} +#define PMS_SLOT_TYPES {MAGNITUDE_PM2dot5, MAGNITUDE_TEMPERATURE, MAGNITUDE_HUMIDITY} +#elif PMS_TYPE == PMS_TYPE_X003_9 +#define PMS_TYPE_NAME "PMSX003_9" +#define PMS_DATA_COUNT 9 +#define PMS_SLOT_COUNT 3 +#define PMS_SLOT_NAMES {"PM1.0", "PM2.5", "PM10"} +#define PMS_SLOT_TYPES {MAGNITUDE_PM1dot0, MAGNITUDE_PM2dot5, MAGNITUDE_PM10} +#else +#define PMS_TYPE_NAME "PMSX003" +#define PMS_DATA_COUNT 13 +#define PMS_SLOT_COUNT 3 +#define PMS_SLOT_NAMES {"PM1.0", "PM2.5", "PM10"} +#define PMS_SLOT_TYPES {MAGNITUDE_PM1dot0, MAGNITUDE_PM2dot5, MAGNITUDE_PM10} +#endif + +#define PMS_PACKET_SIZE ((PMS_DATA_COUNT + 3) * 2) +#define PMS_PAYLOAD_SIZE (PMS_DATA_COUNT * 2 + 2) + + +// PMSX003 sensor utils +// Command functions copied from: https://github.com/fu-hsi/PMS/blob/master/src/PMS.cpp +// Reading function is rewrited to support flexible reading for PMS5003T/PMS5003ST +class PMSX003 { +protected: + SoftwareSerial *_serial = NULL; // Should initialized by child class + +public: + // Standby mode. For low power consumption and prolong the life of the sensor. + inline void sleep() { + uint8_t command[] = { 0x42, 0x4D, 0xE4, 0x00, 0x00, 0x01, 0x73 }; + _serial->write(command, sizeof(command)); + } + + // Operating mode. Stable data should be got at least 30 seconds after the sensor wakeup from the sleep mode because of the fan's performance. + inline void wakeUp() { + uint8_t command[] = { 0x42, 0x4D, 0xE4, 0x00, 0x01, 0x01, 0x74 }; + _serial->write(command, sizeof(command)); + } + + // Active mode. Default mode after power up. In this mode sensor would send serial data to the host automatically. + inline void activeMode() { + uint8_t command[] = { 0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71 }; + _serial->write(command, sizeof(command)); + } + + // Passive mode. In this mode, sensor would send serial data to the host only for request. + inline void passiveMode() { + uint8_t command[] = { 0x42, 0x4D, 0xE1, 0x00, 0x00, 0x01, 0x70 }; + _serial->write(command, sizeof(command)); + } + + // Request read, ONLY needed in Passive Mode!! + inline void requestRead() { + uint8_t command[] = { 0x42, 0x4D, 0xE2, 0x00, 0x00, 0x01, 0x71 }; + _serial->write(command, sizeof(command)); + } + + // Read sensor's data + bool readData(uint16_t data[PMS_DATA_COUNT]) { + do + { + int avail = _serial->available(); + #if SENSOR_DEBUG + //debugSend("%s: Packet available = %d\n", PMS_TYPE_NAME, avail); + #endif + if (avail < PMS_PACKET_SIZE) + break; + + if (_serial->read() == 0x42 && _serial->read() == 0x4D) + { + uint16_t sum = 0x42 + 0x4D; + uint16_t size = read16(sum); + #if SENSOR_DEBUG + debugSend("%s: Payload size = %d\n", PMS_TYPE_NAME, size); + #endif + if (size != PMS_PAYLOAD_SIZE) + { + #if SENSOR_DEBUG + debugSend(("%s: Payload size != %d \n"), PMS_TYPE_NAME, PMS_PAYLOAD_SIZE); + #endif + break; + } + + for (int i = 0; i < PMS_DATA_COUNT; i++) + { + data[i] = read16(sum); + #if SENSOR_DEBUG + //debugSend(("%s: data[%d] = %d\n"), PMS_TYPE_NAME, i, data[i]); + #endif + } + + uint16_t checksum = read16(); + #if SENSOR_DEBUG + debugSend(("%s: Sum=%04X, Checksum=%04X\n"), PMS_TYPE_NAME, sum, checksum); + #endif + if (sum == checksum) + { + return true; + } + break; + } + } + while (true); + return false; + } + +private: + // Read 16-bit + inline uint16_t read16() { + return ((uint16_t) _serial->read()) << 8 | _serial->read(); + } + + // Read 16-bit and calculate checksum + uint16_t read16(uint16_t &checksum) { + uint8_t high = _serial->read(); + uint8_t low = _serial->read(); + checksum += high; + checksum += low; + return ((uint16_t) high) << 8 | low; + } +}; + +class PMSX003Sensor : public BaseSensor, PMSX003 { public: @@ -23,13 +174,12 @@ class PMSX003Sensor : public BaseSensor { // --------------------------------------------------------------------- PMSX003Sensor(): BaseSensor() { - _count = 3; + _count = PMS_SLOT_COUNT; _sensor_id = SENSOR_PMSX003_ID; } ~PMSX003Sensor() { if (_serial) delete _serial; - if (_pms) delete _pms; } void setRX(unsigned char pin_rx) { @@ -64,13 +214,12 @@ class PMSX003Sensor : public BaseSensor { if (!_dirty) return; if (_serial) delete _serial; - if (_pms) delete _pms; - _serial = new SoftwareSerial(_pin_rx, _pin_tx, false, 32); + _serial = new SoftwareSerial(_pin_rx, _pin_tx, false, 64); _serial->enableIntTx(false); _serial->begin(9600); - _pms = new PMS(* _serial); - _pms->passiveMode(); + //_serial->enableRx(true); // TODO: Test + passiveMode(); _startTime = millis(); _ready = true; @@ -81,16 +230,15 @@ class PMSX003Sensor : public BaseSensor { // Descriptive name of the sensor String description() { char buffer[28]; - snprintf(buffer, sizeof(buffer), "PMSX003 @ SwSerial(%u,%u)", _pin_rx, _pin_tx); + snprintf(buffer, sizeof(buffer), "%s @ SwSerial(%u,%u)", PMS_TYPE_NAME, _pin_rx, _pin_tx); return String(buffer); } // Descriptive name of the slot # index String slot(unsigned char index) { char buffer[36] = {0}; - if (index == 0) snprintf(buffer, sizeof(buffer), "PM1.0 @ PMSX003 @ SwSerial(%u,%u)", _pin_rx, _pin_tx); - if (index == 1) snprintf(buffer, sizeof(buffer), "PM2.5 @ PMSX003 @ SwSerial(%u,%u)", _pin_rx, _pin_tx); - if (index == 2) snprintf(buffer, sizeof(buffer), "PM10 @ PMSX003 @ SwSerial(%u,%u)", _pin_rx, _pin_tx); + const static char *_slot_names[] = PMS_SLOT_NAMES; + snprintf(buffer, sizeof(buffer), "%s @ %s @ SwSerial(%u,%u)", _slot_names[index], PMS_TYPE_NAME, _pin_rx, _pin_tx); return String(buffer); } @@ -103,10 +251,8 @@ class PMSX003Sensor : public BaseSensor { // Type for slot # index unsigned char type(unsigned char index) { - if (index == 0) return MAGNITUDE_PM1dot0; - if (index == 1) return MAGNITUDE_PM2dot5; - if (index == 2) return MAGNITUDE_PM10; - return MAGNITUDE_NONE; + const static unsigned char _slot_types[] = PMS_SLOT_TYPES; + return _slot_types[index]; } void pre() { @@ -118,35 +264,71 @@ class PMSX003Sensor : public BaseSensor { _error = SENSOR_ERROR_OK; - if(_pms->read(_data)) { - _pm1dot0 = _data.PM_AE_UG_1_0; - _pm2dot5 = _data.PM_AE_UG_2_5; - _pm10 = _data.PM_AE_UG_10_0; + #if PMS_SMART_SLEEP + unsigned int readCycle; + if (_readCount++ > 30) { + readCycle = _readCount % 30; + if (readCycle == 0) { + #if SENSOR_DEBUG + debugSend("%s: Wake up: %d\n", PMS_TYPE_NAME, _readCount); + #endif + wakeUp(); + return; + } else if (readCycle == 1) { + requestRead(); + } else if (readCycle > 6) { + return; + } + } else { + readCycle = -1; + } + #endif + + uint16_t data[PMS_DATA_COUNT]; + if (readData(data)) { + #if PMS_TYPE == PMS_TYPE_5003ST + _slot_values[0] = data[4]; + _slot_values[1] = (double)data[13] / 10; + _slot_values[2] = (double)data[14] / 10; + _slot_values[3] = (double)data[12] / 1000; + #elif PMS_TYPE == PMS_TYPE_5003T + _slot_values[0] = data[4]; + _slot_values[1] = (double)data[10] / 10; + _slot_values[2] = (double)data[11] / 10; + #else + _slot_values[0] = data[3]; + _slot_values[1] = data[4]; + _slot_values[2] = data[5]; + #endif } - _pms->requestRead(); - + #if PMS_SMART_SLEEP + if (readCycle == 6) { + sleep(); + #if SENSOR_DEBUG + debugSend("%s: Enter sleep mode: %d\n", PMS_TYPE_NAME, _readCount); + #endif + return; + } + #endif + + requestRead(); } // Current value for slot # index double value(unsigned char index) { - if(index == 0) return _pm1dot0; - if(index == 1) return _pm2dot5; - if(index == 2) return _pm10; - return 0; + return _slot_values[index]; } protected: - unsigned int _pm1dot0; - unsigned int _pm2dot5; - unsigned int _pm10; unsigned int _pin_rx; unsigned int _pin_tx; unsigned long _startTime; - SoftwareSerial * _serial = NULL; - PMS * _pms = NULL; - PMS::DATA _data; + double _slot_values[PMS_SLOT_COUNT] = {0}; +#if PMS_SMART_SLEEP + unsigned int _readCount = 0; +#endif }; #endif // SENSOR_SUPPORT && PMSX003_SUPPORT From 4947c259c47eefc8a518cc30fc867e389ba1e44b Mon Sep 17 00:00:00 2001 From: Yonsm Date: Thu, 26 Apr 2018 14:42:34 +0800 Subject: [PATCH 2/5] Add debug msg prefix And found DEBUG_MSG could not be used in the sensors header file. --- code/espurna/sensors/PMSX003Sensor.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/code/espurna/sensors/PMSX003Sensor.h b/code/espurna/sensors/PMSX003Sensor.h index 12d26868..8a7e495d 100644 --- a/code/espurna/sensors/PMSX003Sensor.h +++ b/code/espurna/sensors/PMSX003Sensor.h @@ -106,7 +106,7 @@ public: { int avail = _serial->available(); #if SENSOR_DEBUG - //debugSend("%s: Packet available = %d\n", PMS_TYPE_NAME, avail); + //debugSend("[SENSOR] %s: Packet available = %d\n", PMS_TYPE_NAME, avail); #endif if (avail < PMS_PACKET_SIZE) break; @@ -116,12 +116,12 @@ public: uint16_t sum = 0x42 + 0x4D; uint16_t size = read16(sum); #if SENSOR_DEBUG - debugSend("%s: Payload size = %d\n", PMS_TYPE_NAME, size); + debugSend("[SENSOR] %s: Payload size = %d\n", PMS_TYPE_NAME, size); #endif if (size != PMS_PAYLOAD_SIZE) { #if SENSOR_DEBUG - debugSend(("%s: Payload size != %d \n"), PMS_TYPE_NAME, PMS_PAYLOAD_SIZE); + debugSend(("[SENSOR] %s: Payload size != %d \n"), PMS_TYPE_NAME, PMS_PAYLOAD_SIZE); #endif break; } @@ -130,13 +130,13 @@ public: { data[i] = read16(sum); #if SENSOR_DEBUG - //debugSend(("%s: data[%d] = %d\n"), PMS_TYPE_NAME, i, data[i]); + //debugSend(("[SENSOR] %s: data[%d] = %d\n"), PMS_TYPE_NAME, i, data[i]); #endif } uint16_t checksum = read16(); #if SENSOR_DEBUG - debugSend(("%s: Sum=%04X, Checksum=%04X\n"), PMS_TYPE_NAME, sum, checksum); + debugSend(("[SENSOR] %s: Sum=%04X, Checksum=%04X\n"), PMS_TYPE_NAME, sum, checksum); #endif if (sum == checksum) { @@ -270,7 +270,7 @@ class PMSX003Sensor : public BaseSensor, PMSX003 { readCycle = _readCount % 30; if (readCycle == 0) { #if SENSOR_DEBUG - debugSend("%s: Wake up: %d\n", PMS_TYPE_NAME, _readCount); + debugSend("[SENSOR] %s: Wake up: %d\n", PMS_TYPE_NAME, _readCount); #endif wakeUp(); return; @@ -306,7 +306,7 @@ class PMSX003Sensor : public BaseSensor, PMSX003 { if (readCycle == 6) { sleep(); #if SENSOR_DEBUG - debugSend("%s: Enter sleep mode: %d\n", PMS_TYPE_NAME, _readCount); + debugSend("[SENSOR] %s: Enter sleep mode: %d\n", PMS_TYPE_NAME, _readCount); #endif return; } From e2d3af53cc10cc976db78e8f21e89ba0b000f546 Mon Sep 17 00:00:00 2001 From: Yonsm Date: Thu, 26 Apr 2018 22:02:03 +0800 Subject: [PATCH 3/5] Remove unnecessary comment code --- code/espurna/sensors/PMSX003Sensor.h | 1 - 1 file changed, 1 deletion(-) diff --git a/code/espurna/sensors/PMSX003Sensor.h b/code/espurna/sensors/PMSX003Sensor.h index 8a7e495d..35b36df5 100644 --- a/code/espurna/sensors/PMSX003Sensor.h +++ b/code/espurna/sensors/PMSX003Sensor.h @@ -218,7 +218,6 @@ class PMSX003Sensor : public BaseSensor, PMSX003 { _serial = new SoftwareSerial(_pin_rx, _pin_tx, false, 64); _serial->enableIntTx(false); _serial->begin(9600); - //_serial->enableRx(true); // TODO: Test passiveMode(); _startTime = millis(); From d009c8a962dcdb59b095e3e08daa6f5e5ac24c85 Mon Sep 17 00:00:00 2001 From: Yonsm Date: Thu, 26 Apr 2018 22:06:42 +0800 Subject: [PATCH 4/5] Remove PMS Library dependency --- code/platformio.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/code/platformio.ini b/code/platformio.ini index db3a4034..1bfbd65b 100644 --- a/code/platformio.ini +++ b/code/platformio.ini @@ -70,7 +70,6 @@ lib_deps = https://bitbucket.org/xoseperez/nofuss.git#0.2.5 https://github.com/xoseperez/NtpClient.git#0016a59 OneWire - PMS Library PZEM004T PubSubClient rc-switch From 230e087709326a0afecaee059135e1f0d88ce456 Mon Sep 17 00:00:00 2001 From: Yonsm Date: Thu, 26 Apr 2018 22:11:42 +0800 Subject: [PATCH 5/5] Add file header comment --- code/espurna/sensors/PMSX003Sensor.h | 1 + 1 file changed, 1 insertion(+) diff --git a/code/espurna/sensors/PMSX003Sensor.h b/code/espurna/sensors/PMSX003Sensor.h index 35b36df5..c9e8463e 100644 --- a/code/espurna/sensors/PMSX003Sensor.h +++ b/code/espurna/sensors/PMSX003Sensor.h @@ -2,6 +2,7 @@ // PMSX003 Dust Sensor // Uses SoftwareSerial library // Contribution by Òscar Rovira López +// Refine to support PMS5003T/PMS5003ST by Yonsm Guo // ----------------------------------------------------------------------------- #if SENSOR_SUPPORT && PMSX003_SUPPORT