#include "PMS.h" #include "../Main/BoardDef.h" /** * @brief Init and check that sensor has connected * * @param stream UART stream * @return true Sucecss * @return false Failure */ bool PMSBase::begin(Stream *stream) { this->stream = stream; failed = true; lastRead = 0; // To read buffer on handle without wait after 1.5sec this->stream->flush(); // Run and check sensor data for 4sec while (1) { handle(); if (failed == false) { return true; } delay(1); uint32_t ms = (uint32_t)(millis() - lastRead); if (ms >= 4000) { break; } } return false; } /** * @brief Check and read sensor data then update variable. * Check result from method @isFailed before get value */ 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; while (stream->available()) { char value = stream->read(); switch (step) { case 0: { if (value == 0x42) { step = 1; bufIndex = 0; buf[bufIndex++] = value; } break; } case 1: { if (value == 0x4d) { step = 2; buf[bufIndex++] = value; // Serial.println("Got 0x4d"); } else { step = 0; } break; } case 2: { buf[bufIndex++] = value; if (bufIndex >= 4) { len = toValue(&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; } } 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); } } if (result) { lastRead = millis(); if (lastRead == 0) { lastRead = 1; } failed = false; } else { if (ms > 5000) { failed = true; } } } /** * @brief Check that PMS send is failed or disconnected * * @return true Failed * @return false No problem */ bool PMSBase::isFailed(void) { return failed; } /** * @brief Read PMS 0.1 ug/m3 with CF = 1 PM estimates * * @return uint16_t */ uint16_t PMSBase::getRaw0_1(void) { return toValue(&package[4]); } /** * @brief Read PMS 2.5 ug/m3 with CF = 1 PM estimates * * @return uint16_t */ uint16_t PMSBase::getRaw2_5(void) { return toValue(&package[6]); } /** * @brief Read PMS 10 ug/m3 with CF = 1 PM estimates * * @return uint16_t */ uint16_t PMSBase::getRaw10(void) { return toValue(&package[8]); } /** * @brief Read PMS 0.1 ug/m3 * * @return uint16_t */ uint16_t PMSBase::getPM0_1(void) { return toValue(&package[10]); } /** * @brief Read PMS 2.5 ug/m3 * * @return uint16_t */ uint16_t PMSBase::getPM2_5(void) { return toValue(&package[12]); } /** * @brief Read PMS 10 ug/m3 * * @return uint16_t */ uint16_t PMSBase::getPM10(void) { return toValue(&package[14]); } /** * @brief Get numnber concentrations over 0.3 um/0.1L * * @return uint16_t */ uint16_t PMSBase::getCount0_3(void) { return toValue(&package[16]); } /** * @brief Get numnber concentrations over 0.5 um/0.1L * * @return uint16_t */ uint16_t PMSBase::getCount0_5(void) { return toValue(&package[18]); } /** * @brief Get numnber concentrations over 1.0 um/0.1L * * @return uint16_t */ uint16_t PMSBase::getCount1_0(void) { return toValue(&package[20]); } /** * @brief Get numnber concentrations over 2.5 um/0.1L * * @return uint16_t */ uint16_t PMSBase::getCount2_5(void) { return toValue(&package[22]); } /** * @brief Get numnber concentrations over 5.0 um/0.1L (only PMS5003) * * @return uint16_t */ uint16_t PMSBase::getCount5_0(void) { return toValue(&package[24]); } /** * @brief Get numnber concentrations over 10.0 um/0.1L (only PMS5003) * * @return uint16_t */ uint16_t PMSBase::getCount10(void) { return toValue(&package[26]); } /** * @brief Get temperature (only PMS5003T) * * @return uint16_t */ uint16_t PMSBase::getTemp(void) { return toValue(&package[24]); } /** * @brief Get humidity (only PMS5003T) * * @return uint16_t */ uint16_t PMSBase::getHum(void) { return toValue(&package[26]); } /** * @brief Convert PMS2.5 to US AQI unit * * @param pm02 * @return int */ int PMSBase::pm25ToAQI(int pm02) { if (pm02 <= 12.0) return ((50 - 0) / (12.0 - .0) * (pm02 - .0) + 0); else if (pm02 <= 35.4) return ((100 - 50) / (35.4 - 12.0) * (pm02 - 12.0) + 50); else if (pm02 <= 55.4) return ((150 - 100) / (55.4 - 35.4) * (pm02 - 35.4) + 100); else if (pm02 <= 150.4) return ((200 - 150) / (150.4 - 55.4) * (pm02 - 55.4) + 150); else if (pm02 <= 250.4) return ((300 - 200) / (250.4 - 150.4) * (pm02 - 150.4) + 200); else if (pm02 <= 350.4) return ((400 - 300) / (350.4 - 250.4) * (pm02 - 250.4) + 300); else if (pm02 <= 500.4) return ((500 - 400) / (500.4 - 350.4) * (pm02 - 350.4) + 400); else return 500; } /** * @brief Convert two byte value to uint16_t value * * @param buf bytes array (must be >= 2) * @return uint16_t */ uint16_t PMSBase::toValue(char *buf) { return (buf[0] << 8) | buf[1]; } /** * @brief Validate package data * * @param buf Package buffer * @return true Success * @return false Failed */ bool PMSBase::validate(char *buf) { uint16_t sum = 0; for (int i = 0; i < 30; i++) { sum += buf[i]; } if (sum == toValue(&buf[30])) { for (int i = 0; i < 32; i++) { package[i] = buf[i]; } return true; } return false; }