diff --git a/examples/OneOpenAir/OneOpenAir.ino b/examples/OneOpenAir/OneOpenAir.ino index ee7314e..45ab1c1 100644 --- a/examples/OneOpenAir/OneOpenAir.ino +++ b/examples/OneOpenAir/OneOpenAir.ino @@ -288,6 +288,11 @@ void loop() { if (ag->isOne()) { if (configuration.hasSensorPMS1) { ag->pms5003.handle(); + static bool pmsConnected = false; + if (pmsConnected != ag->pms5003.connected()) { + pmsConnected = ag->pms5003.connected(); + Serial.printf("PMS sensor %s ", pmsConnected?"connected":"removed"); + } } } else { if (configuration.hasSensorPMS1) { @@ -1009,7 +1014,7 @@ static void updateTvoc(void) { static void updatePm(void) { bool restart = false; if (ag->isOne()) { - if (ag->pms5003.isFailed() == false) { + if (ag->pms5003.connected()) { measurements.pm01_1 = ag->pms5003.getPm01Ae(); measurements.pm25_1 = ag->pms5003.getPm25Ae(); measurements.pm10_1 = ag->pms5003.getPm10Ae(); @@ -1038,7 +1043,7 @@ static void updatePm(void) { } else { bool pmsResult_1 = false; bool pmsResult_2 = false; - if (configuration.hasSensorPMS1 && (ag->pms5003t_1.isFailed() == false)) { + if (configuration.hasSensorPMS1 && ag->pms5003t_1.connected()) { measurements.pm01_1 = ag->pms5003t_1.getPm01Ae(); measurements.pm25_1 = ag->pms5003t_1.getPm25Ae(); measurements.pm10_1 = ag->pms5003t_1.getPm10Ae(); @@ -1081,7 +1086,7 @@ static void updatePm(void) { } } - if (configuration.hasSensorPMS2 && (ag->pms5003t_2.isFailed() == false)) { + if (configuration.hasSensorPMS2 && ag->pms5003t_2.connected()) { measurements.pm01_2 = ag->pms5003t_2.getPm01Ae(); measurements.pm25_2 = ag->pms5003t_2.getPm25Ae(); measurements.pm10_2 = ag->pms5003t_2.getPm10Ae(); diff --git a/src/PMS/PMS.cpp b/src/PMS/PMS.cpp index 1107185..9fefb55 100644 --- a/src/PMS/PMS.cpp +++ b/src/PMS/PMS.cpp @@ -8,146 +8,111 @@ * @return true Sucecss * @return false Failure */ -bool PMSBase::begin(Stream *stream) { - this->stream = stream; - - failed = true; +bool PMSBase::begin(HardwareSerial& serial) { failCount = 0; - lastRead = 0; // To read buffer on handle without wait after 1.5sec - - this->stream->flush(); + _connected = false; // Run and check sensor data for 4sec - while (1) { - handle(); - if (failed == false) { - return true; + serial.flush(); + + unsigned long lastInit = millis(); + while (true) { + readPackage(serial); + if (_connected) { + break; } delay(1); - uint32_t ms = (uint32_t)(millis() - lastRead); + unsigned long ms = (unsigned long)(millis() - lastInit); if (ms >= 4000) { break; } } - return false; + return _connected; } /** - * @brief Check and read sensor data then update variable. - * Check result from method @isFailed before get value + * @brief Read PMS package send to device each 1sec + * + * @param serial */ -void PMSBase::handle() { - uint32_t ms; - if (lastRead == 0) { - lastRead = millis(); - if (lastRead == 0) { - lastRead = 1; - } - } else { - ms = (uint32_t)(millis() - lastRead); - /** - * The PMS in Active mode sends an update data every 1 second. If we read - * exactly every 1 sec then we may or may not get an update (depending on - * timing tolerances). Hence we read every 2.5 seconds and expect 2 ..3 - * updates, - */ - if (ms < 2500) { - return; - } - } - bool result = false; - char buf[32]; - int bufIndex; - int step = 0; - int len = 0; - int bcount = 0; +void PMSBase::readPackage(HardwareSerial& serial) { + uint8_t delayCount = 0; + while (serial.available()) { + /** Get value */ + uint8_t value = (uint8_t)serial.read(); - while (stream->available()) { - char value = stream->read(); - switch (step) { - case 0: { + /** Process receiving package... */ + switch (readBufferIndex) { + case 0: /** Start byte 1 */ if (value == 0x42) { - step = 1; - bufIndex = 0; - buf[bufIndex++] = value; + readBuffer[readBufferIndex++] = value; } break; - } - case 1: { + case 1: /** Start byte 2 */ if (value == 0x4d) { - step = 2; - buf[bufIndex++] = value; - // Serial.println("Got 0x4d"); + readBuffer[readBufferIndex++] = value; } else { - step = 0; + readBufferIndex = 0; } break; - } - case 2: { - buf[bufIndex++] = value; - if (bufIndex >= 4) { - len = toI16(&buf[2]); - if (len != 28) { - // Serial.printf("Got good bad len %d\r\n", len); - len += 4; - step = 3; - } else { - // Serial.println("Got good len"); - step = 4; + case 2: /** Frame length */ + if (value == 0x00) { + readBuffer[readBufferIndex++] = value; + } else { + readBufferIndex = 0; + } + break; + case 3: /** Frame length */ + if (value == 0x1C) { + readBuffer[readBufferIndex++] = value; + } else { + readBufferIndex = 0; + } + break; + default: /** Data */ + readBuffer[readBufferIndex++] = value; + + /** Check that received full bufer */ + if (readBufferIndex >= sizeof(readBuffer)) { + /** validata package */ + if (validate(readBuffer)) { + _connected = true; /** Set connected status */ + + /** Parse data */ + parse(readBuffer); + + /** Set last received package */ + lastPackage = millis(); + if (lastPackage == 0) { + lastPackage = 1; + } } + + /** Clear buffer index */ + readBufferIndex = 0; } break; } - case 3: { - bufIndex++; - if (bufIndex >= len) { - step = 0; - // Serial.println("Bad lengh read all buffer"); - } - break; - } - case 4: { - buf[bufIndex++] = value; - if (bufIndex >= 32) { - result |= validate(buf); - step = 0; - // Serial.println("Got data"); - } - break; - } - default: - break; - } - // Reduce core panic: delay 1 ms each 32bytes data - bcount++; - if ((bcount % 32) == 0) { - delay(1); + /** Avoid task watchdog timer reset... */ + delayCount++; + if(delayCount >= 32) + { + delay(1); } } - if (result) { - lastRead = millis(); - if (lastRead == 0) { - lastRead = 1; - } - failed = false; - } else { - if (ms > 5000) { - failed = true; + /** Check that sensor removed */ + if (lastPackage) { + unsigned long ms = (unsigned long)(millis() - lastPackage); + if (ms >= 1500) { + lastPackage = 0; + _connected = false; } } } -/** - * @brief Check that PMS send is failed or disconnected - * - * @return true Failed - * @return false No problem - */ -bool PMSBase::isFailed(void) { return failed; } - /** * @brief Increate number of fail * @@ -174,112 +139,114 @@ int PMSBase::getFailCountMax(void) { return failCountMax; } * * @return uint16_t */ -uint16_t PMSBase::getRaw0_1(void) { return toU16(&package[4]); } +uint16_t PMSBase::getRaw0_1(void) { return pms_raw0_1; } /** * @brief Read PMS 2.5 ug/m3 with CF = 1 PM estimates * * @return uint16_t */ -uint16_t PMSBase::getRaw2_5(void) { return toU16(&package[6]); } +uint16_t PMSBase::getRaw2_5(void) { return pms_raw2_5; } /** * @brief Read PMS 10 ug/m3 with CF = 1 PM estimates * * @return uint16_t */ -uint16_t PMSBase::getRaw10(void) { return toU16(&package[8]); } +uint16_t PMSBase::getRaw10(void) { return pms_raw10; } /** * @brief Read PMS 0.1 ug/m3 * * @return uint16_t */ -uint16_t PMSBase::getPM0_1(void) { return toU16(&package[10]); } +uint16_t PMSBase::getPM0_1(void) { return pms_pm0_1; } /** * @brief Read PMS 2.5 ug/m3 * * @return uint16_t */ -uint16_t PMSBase::getPM2_5(void) { return toU16(&package[12]); } +uint16_t PMSBase::getPM2_5(void) { return pms_pm2_5; } /** * @brief Read PMS 10 ug/m3 * * @return uint16_t */ -uint16_t PMSBase::getPM10(void) { return toU16(&package[14]); } +uint16_t PMSBase::getPM10(void) { return pms_pm10; } /** * @brief Get numnber concentrations over 0.3 um/0.1L * * @return uint16_t */ -uint16_t PMSBase::getCount0_3(void) { return toU16(&package[16]); } +uint16_t PMSBase::getCount0_3(void) { return pms_count0_3; } /** * @brief Get numnber concentrations over 0.5 um/0.1L * * @return uint16_t */ -uint16_t PMSBase::getCount0_5(void) { return toU16(&package[18]); } +uint16_t PMSBase::getCount0_5(void) { return pms_count0_5; } /** * @brief Get numnber concentrations over 1.0 um/0.1L * * @return uint16_t */ -uint16_t PMSBase::getCount1_0(void) { return toU16(&package[20]); } +uint16_t PMSBase::getCount1_0(void) { return pms_count1_0; } /** * @brief Get numnber concentrations over 2.5 um/0.1L * * @return uint16_t */ -uint16_t PMSBase::getCount2_5(void) { return toU16(&package[22]); } +uint16_t PMSBase::getCount2_5(void) { return pms_count2_5; } + +bool PMSBase::connected(void) { return _connected; } /** * @brief Get numnber concentrations over 5.0 um/0.1L (only PMS5003) * * @return uint16_t */ -uint16_t PMSBase::getCount5_0(void) { return toU16(&package[24]); } +uint16_t PMSBase::getCount5_0(void) { return pms_count5_0; } /** * @brief Get numnber concentrations over 10.0 um/0.1L (only PMS5003) * * @return uint16_t */ -uint16_t PMSBase::getCount10(void) { return toU16(&package[26]); } +uint16_t PMSBase::getCount10(void) { return pms_count10; } /** * @brief Get temperature (only PMS5003T) * * @return uint16_t */ -int16_t PMSBase::getTemp(void) { return toI16(&package[24]); } +int16_t PMSBase::getTemp(void) { return pms_temp; } /** * @brief Get humidity (only PMS5003T) * * @return uint16_t */ -uint16_t PMSBase::getHum(void) { return toU16(&package[26]); } +uint16_t PMSBase::getHum(void) { return pms_hum; } /** * @brief Get firmware version code * * @return uint8_t */ -uint8_t PMSBase::getFirmwareVersion(void) { return package[28]; } +uint8_t PMSBase::getFirmwareVersion(void) { return pms_firmwareVersion; } /** * @brief Ge PMS5003 error code * * @return uint8_t */ -uint8_t PMSBase::getErrorCode(void) { return package[29]; } +uint8_t PMSBase::getErrorCode(void) { return pms_errorCode; } /** * @brief Convert PMS2.5 to US AQI unit @@ -350,13 +317,13 @@ int PMSBase::compensate(int pm25, float humidity) { * @param buf bytes array (must be >= 2) * @return int16_t */ -int16_t PMSBase::toI16(char *buf) { +int16_t PMSBase::toI16(const uint8_t *buf) { int16_t value = buf[0]; value = (value << 8) | buf[1]; return value; } -uint16_t PMSBase::toU16(char *buf) { +uint16_t PMSBase::toU16(const uint8_t *buf) { uint16_t value = buf[0]; value = (value << 8) | buf[1]; return value; @@ -369,16 +336,32 @@ uint16_t PMSBase::toU16(char *buf) { * @return true Success * @return false Failed */ -bool PMSBase::validate(char *buf) { +bool PMSBase::validate(const uint8_t *buf) { uint16_t sum = 0; for (int i = 0; i < 30; i++) { sum += buf[i]; } if (sum == toU16(&buf[30])) { - for (int i = 0; i < 32; i++) { - package[i] = buf[i]; - } return true; } return false; } + +void PMSBase::parse(const uint8_t *buf) { + pms_raw0_1 = toU16(&buf[4]); + pms_raw2_5 = toU16(&buf[6]); + pms_raw10 = toU16(&buf[8]); + pms_pm0_1 = toU16(&buf[10]); + pms_pm2_5 = toU16(&buf[12]); + pms_pm10 = toU16(&buf[14]); + pms_count0_3 = toU16(&buf[16]); + pms_count0_5 = toU16(&buf[18]); + pms_count1_0 = toU16(&buf[20]); + pms_count2_5 = toU16(&buf[22]); + pms_count5_0 = toU16(&buf[24]); + pms_count10 = toU16(&buf[26]); + pms_temp = toU16(&buf[24]); + pms_hum = toU16(&buf[26]); + pms_firmwareVersion = buf[28]; + pms_errorCode = buf[29]; +} diff --git a/src/PMS/PMS.h b/src/PMS/PMS.h index e79480e..3e6ec9f 100644 --- a/src/PMS/PMS.h +++ b/src/PMS/PMS.h @@ -7,9 +7,8 @@ class PMSBase { public: - bool begin(Stream *stream); - void handle(); - bool isFailed(void); + bool begin(HardwareSerial& serial); + void readPackage(HardwareSerial& serial); void updateFailCount(void); void resetFailCount(void); int getFailCount(void); @@ -24,6 +23,7 @@ public: uint16_t getCount0_5(void); uint16_t getCount1_0(void); uint16_t getCount2_5(void); + bool connected(void); /** For PMS5003 */ uint16_t getCount5_0(void); @@ -39,17 +39,36 @@ public: int compensate(int pm25, float humidity); private: - Stream *stream; - char package[32]; - int packageIndex; - bool failed = false; - uint32_t lastRead; + static const uint8_t package_size = 32; const int failCountMax = 10; int failCount = 0; - int16_t toI16(char *buf); - uint16_t toU16(char* buf); - bool validate(char *buf); + uint8_t readBuffer[package_size]; + uint8_t readBufferIndex = 0; + unsigned long lastPackage = 0; + bool _connected; + + uint16_t pms_raw0_1; + uint16_t pms_raw2_5; + uint16_t pms_raw10; + uint16_t pms_pm0_1; + uint16_t pms_pm2_5; + uint16_t pms_pm10; + uint16_t pms_count0_3; + uint16_t pms_count0_5; + uint16_t pms_count1_0; + uint16_t pms_count2_5; + uint16_t pms_count5_0; + uint16_t pms_count10; + int16_t pms_temp; + uint16_t pms_hum; + uint8_t pms_errorCode; + uint8_t pms_firmwareVersion; + + int16_t toI16(const uint8_t *buf); + uint16_t toU16(const uint8_t *buf); + bool validate(const uint8_t *buf); + void parse(const uint8_t* buf); }; #endif /** _PMS5003_BASE_H_ */ diff --git a/src/PMS/PMS5003.cpp b/src/PMS/PMS5003.cpp index b632627..3e52896 100644 --- a/src/PMS/PMS5003.cpp +++ b/src/PMS/PMS5003.cpp @@ -73,7 +73,7 @@ bool PMS5003::begin(void) { } #else this->_serial->begin(9600); - if (pms.begin(this->_serial) == false) { + if (pms.begin(*this->_serial) == false) { AgLog("PMS failed"); return false; } @@ -148,6 +148,14 @@ int PMS5003::getFirmwareVersion(void) { return _ver; } */ uint8_t PMS5003::getErrorCode(void) { return pms.getErrorCode(); } +/** + * @brief Is sensor connect with device + * + * @return true Connected + * @return false Removed + */ +bool PMS5003::connected(void) { return pms.connected(); } + /** * @brief Check device initialized or not * @@ -182,15 +190,7 @@ void PMS5003::end(void) { * @brief Check and read PMS sensor data. This method should be callack from * loop process to continoue check sensor data if it's available */ -void PMS5003::handle(void) { pms.handle(); } - -/** - * @brief Get sensor status - * - * @return true No problem - * @return false Communication timeout or sensor has removed - */ -bool PMS5003::isFailed(void) { return pms.isFailed(); } +void PMS5003::handle(void) { pms.readPackage(*this->_serial); } void PMS5003::updateFailCount(void) { pms.updateFailCount(); diff --git a/src/PMS/PMS5003.h b/src/PMS/PMS5003.h index 88fda3b..66af3a7 100644 --- a/src/PMS/PMS5003.h +++ b/src/PMS/PMS5003.h @@ -18,7 +18,6 @@ public: #endif void end(void); void handle(void); - bool isFailed(void); void updateFailCount(void); void resetFailCount(void); int getFailCount(void); @@ -31,6 +30,7 @@ public: int compensate(int pm25, float humidity); int getFirmwareVersion(void); uint8_t getErrorCode(void); + bool connected(void); private: bool _isBegin = false; diff --git a/src/PMS/PMS5003T.cpp b/src/PMS/PMS5003T.cpp index b5e65f3..0e1a8bf 100644 --- a/src/PMS/PMS5003T.cpp +++ b/src/PMS/PMS5003T.cpp @@ -98,7 +98,7 @@ bool PMS5003T::begin(void) { this->_serial->begin(9600, SERIAL_8N1, bsp->SenseAirS8.uart_rx_pin, bsp->SenseAirS8.uart_tx_pin); } - if (pms.begin(this->_serial) == false) { + if (pms.begin(*this->_serial) == false) { AgLog("PMS failed"); return false; } @@ -191,6 +191,14 @@ int PMS5003T::getFirmwareVersion(void) { return _ver; } */ uint8_t PMS5003T::getErrorCode(void) { return pms.getErrorCode(); } +/** + * @brief Is sensor connect to device + * + * @return true Connected + * @return false Removed + */ +bool PMS5003T::connected(void) { return pms.connected(); } + /** * @brief Check device initialized or not * @@ -222,15 +230,7 @@ void PMS5003T::end(void) { * @brief Check and read PMS sensor data. This method should be callack from * loop process to continoue check sensor data if it's available */ -void PMS5003T::handle(void) { pms.handle(); } - -/** - * @brief Get sensor status - * - * @return true No problem - * @return false Communication timeout or sensor has removed - */ -bool PMS5003T::isFailed(void) { return pms.isFailed(); } +void PMS5003T::handle(void) { pms.readPackage(*this->_serial); } void PMS5003T::updateFailCount(void) { pms.updateFailCount(); diff --git a/src/PMS/PMS5003T.h b/src/PMS/PMS5003T.h index a35a605..ea9cbed 100644 --- a/src/PMS/PMS5003T.h +++ b/src/PMS/PMS5003T.h @@ -21,7 +21,6 @@ public: void end(void); void handle(void); - bool isFailed(void); void updateFailCount(void); void resetFailCount(void); int getFailCount(void); @@ -36,6 +35,7 @@ public: int compensate(int pm25, float humidity); int getFirmwareVersion(void); uint8_t getErrorCode(void); + bool connected(void); private: bool _isBegin = false;