|
|
@ -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 |
|
|
@ -14,7 +15,158 @@ |
|
|
|
#include <PMS.h> |
|
|
|
#include <SoftwareSerial.h> |
|
|
|
|
|
|
|
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("[SENSOR] %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("[SENSOR] %s: Payload size = %d\n", PMS_TYPE_NAME, size); |
|
|
|
#endif |
|
|
|
if (size != PMS_PAYLOAD_SIZE) |
|
|
|
{ |
|
|
|
#if SENSOR_DEBUG |
|
|
|
debugSend(("[SENSOR] %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(("[SENSOR] %s: data[%d] = %d\n"), PMS_TYPE_NAME, i, data[i]); |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
uint16_t checksum = read16(); |
|
|
|
#if SENSOR_DEBUG |
|
|
|
debugSend(("[SENSOR] %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 +175,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 +215,11 @@ 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(); |
|
|
|
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("[SENSOR] %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("[SENSOR] %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 |