2024-02-17 17:28:51 +07:00
|
|
|
#include "PMS.h"
|
2024-03-14 21:17:43 +07:00
|
|
|
#include "../Main/BoardDef.h"
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-03-16 08:21:14 +07:00
|
|
|
/**
|
2024-09-15 08:26:38 +07:00
|
|
|
* @brief Initializes the sensor and attempts to read data.
|
2024-08-15 09:10:48 +07:00
|
|
|
*
|
2024-09-24 20:07:14 +07:00
|
|
|
* @param stream UART stream
|
2024-03-16 08:21:14 +07:00
|
|
|
* @return true Sucecss
|
|
|
|
* @return false Failure
|
|
|
|
*/
|
2024-09-24 20:07:14 +07:00
|
|
|
bool PMSBase::begin(Stream *stream) {
|
|
|
|
Serial.printf("initializing PM sensor\n");
|
|
|
|
|
2024-08-25 20:21:26 +07:00
|
|
|
failCount = 0;
|
2024-09-14 14:05:35 +07:00
|
|
|
_connected = false;
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-09-24 20:07:14 +07:00
|
|
|
// empty first
|
|
|
|
int bytesCleared = 0;
|
|
|
|
while (stream->read() != -1) {
|
|
|
|
bytesCleared++;
|
2024-09-24 10:39:17 +07:00
|
|
|
}
|
2024-09-24 20:07:14 +07:00
|
|
|
Serial.printf("cleared %d byte(s)\n", bytesCleared);
|
2024-09-24 10:39:17 +07:00
|
|
|
|
2024-09-24 20:07:14 +07:00
|
|
|
// explicitly put the sensor into active mode, this seems to be be needed for the Cubic PM2009X
|
|
|
|
Serial.printf("setting active mode\n");
|
|
|
|
uint8_t activeModeCommand[] = { 0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71 };
|
|
|
|
size_t bytesWritten = stream->write(activeModeCommand, sizeof(activeModeCommand));
|
2024-09-24 10:39:17 +07:00
|
|
|
Serial.printf("%d byte(s) written\n", bytesWritten);
|
2024-09-14 14:05:35 +07:00
|
|
|
|
2024-09-24 20:07:14 +07:00
|
|
|
// Run and check sensor data for 4sec
|
2024-09-14 14:05:35 +07:00
|
|
|
unsigned long lastInit = millis();
|
|
|
|
while (true) {
|
2024-09-24 20:07:14 +07:00
|
|
|
readPackage(stream);
|
2024-09-14 14:05:35 +07:00
|
|
|
if (_connected) {
|
|
|
|
break;
|
2024-03-14 21:17:43 +07:00
|
|
|
}
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
delay(1);
|
2024-09-14 14:05:35 +07:00
|
|
|
unsigned long ms = (unsigned long)(millis() - lastInit);
|
2024-03-14 21:17:43 +07:00
|
|
|
if (ms >= 4000) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2024-09-14 14:05:35 +07:00
|
|
|
return _connected;
|
2024-02-17 17:28:51 +07:00
|
|
|
}
|
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
2024-09-14 14:05:35 +07:00
|
|
|
* @brief Read PMS package send to device each 1sec
|
2024-09-21 17:48:18 +07:00
|
|
|
*
|
|
|
|
* @param serial
|
2024-03-14 21:17:43 +07:00
|
|
|
*/
|
2024-09-24 20:07:14 +07:00
|
|
|
void PMSBase::readPackage(Stream *serial) {
|
2024-09-24 10:28:41 +07:00
|
|
|
/** If readPackage has process as period larger than READ_PACKAGE_TIMEOUT,
|
|
|
|
* should be clear the lastPackage and readBufferIndex */
|
|
|
|
if (lastReadPackage) {
|
|
|
|
unsigned long ms = (unsigned long)(millis() - lastReadPackage);
|
|
|
|
if (ms >= READ_PACKGE_TIMEOUT) {
|
|
|
|
/** Clear buffer */
|
|
|
|
readBufferIndex = 0;
|
|
|
|
|
|
|
|
/** Disable check read package timeout */
|
|
|
|
lastPackage = 0;
|
|
|
|
|
|
|
|
Serial.println("Last process timeout, clear buffer and last handle package");
|
|
|
|
}
|
|
|
|
|
|
|
|
lastReadPackage = millis();
|
|
|
|
if (!lastReadPackage) {
|
|
|
|
lastReadPackage = 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
lastReadPackage = millis();
|
|
|
|
if (!lastReadPackage) {
|
|
|
|
lastReadPackage = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Count to call delay() to release the while loop MCU resource for avoid the
|
|
|
|
* watchdog time reset */
|
2024-09-14 14:05:35 +07:00
|
|
|
uint8_t delayCount = 0;
|
2024-09-24 20:07:14 +07:00
|
|
|
while (serial->available()) {
|
2024-09-14 14:05:35 +07:00
|
|
|
/** Get value */
|
2024-09-24 20:07:14 +07:00
|
|
|
uint8_t value = (uint8_t)serial->read();
|
2024-09-14 14:05:35 +07:00
|
|
|
|
|
|
|
/** Process receiving package... */
|
|
|
|
switch (readBufferIndex) {
|
|
|
|
case 0: /** Start byte 1 */
|
2024-03-14 21:17:43 +07:00
|
|
|
if (value == 0x42) {
|
2024-09-14 14:05:35 +07:00
|
|
|
readBuffer[readBufferIndex++] = value;
|
2024-03-14 21:17:43 +07:00
|
|
|
}
|
|
|
|
break;
|
2024-09-14 14:05:35 +07:00
|
|
|
case 1: /** Start byte 2 */
|
2024-03-14 21:17:43 +07:00
|
|
|
if (value == 0x4d) {
|
2024-09-14 14:05:35 +07:00
|
|
|
readBuffer[readBufferIndex++] = value;
|
2024-03-14 21:17:43 +07:00
|
|
|
} else {
|
2024-09-14 14:05:35 +07:00
|
|
|
readBufferIndex = 0;
|
2024-03-14 21:17:43 +07:00
|
|
|
}
|
|
|
|
break;
|
2024-09-14 14:05:35 +07:00
|
|
|
case 2: /** Frame length */
|
|
|
|
if (value == 0x00) {
|
|
|
|
readBuffer[readBufferIndex++] = value;
|
|
|
|
} else {
|
|
|
|
readBufferIndex = 0;
|
2024-03-14 21:17:43 +07:00
|
|
|
}
|
|
|
|
break;
|
2024-09-14 14:05:35 +07:00
|
|
|
case 3: /** Frame length */
|
|
|
|
if (value == 0x1C) {
|
|
|
|
readBuffer[readBufferIndex++] = value;
|
|
|
|
} else {
|
|
|
|
readBufferIndex = 0;
|
2024-03-14 21:17:43 +07:00
|
|
|
}
|
|
|
|
break;
|
2024-09-14 14:05:35 +07:00
|
|
|
default: /** Data */
|
2024-09-21 17:47:59 +07:00
|
|
|
{
|
2024-09-14 14:05:35 +07:00
|
|
|
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;
|
2024-03-14 21:17:43 +07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2024-09-21 17:47:59 +07:00
|
|
|
}
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-09-14 14:05:35 +07:00
|
|
|
/** Avoid task watchdog timer reset... */
|
|
|
|
delayCount++;
|
2024-09-21 17:47:59 +07:00
|
|
|
if (delayCount >= 32) {
|
|
|
|
delayCount = 0;
|
|
|
|
delay(1);
|
2024-03-14 21:17:43 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-14 14:05:35 +07:00
|
|
|
/** Check that sensor removed */
|
|
|
|
if (lastPackage) {
|
|
|
|
unsigned long ms = (unsigned long)(millis() - lastPackage);
|
2024-09-24 10:28:41 +07:00
|
|
|
if (ms >= READ_PACKGE_TIMEOUT) {
|
2024-09-14 14:05:35 +07:00
|
|
|
lastPackage = 0;
|
|
|
|
_connected = false;
|
2024-10-31 21:16:29 +07:00
|
|
|
Serial.println("PMS disconnected");
|
2024-03-14 21:17:43 +07:00
|
|
|
}
|
2024-02-17 17:28:51 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-25 20:21:26 +07:00
|
|
|
/**
|
|
|
|
* @brief Increate number of fail
|
2024-09-21 17:48:18 +07:00
|
|
|
*
|
2024-08-25 20:21:26 +07:00
|
|
|
*/
|
|
|
|
void PMSBase::updateFailCount(void) {
|
|
|
|
if (failCount < failCountMax) {
|
|
|
|
failCount++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-26 14:14:42 +07:00
|
|
|
void PMSBase::resetFailCount(void) { failCount = 0; }
|
2024-08-25 20:21:26 +07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get number of fail
|
2024-09-21 17:48:18 +07:00
|
|
|
*
|
|
|
|
* @return int
|
2024-08-25 20:21:26 +07:00
|
|
|
*/
|
|
|
|
int PMSBase::getFailCount(void) { return failCount; }
|
|
|
|
|
|
|
|
int PMSBase::getFailCountMax(void) { return failCountMax; }
|
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Read PMS 0.1 ug/m3 with CF = 1 PM estimates
|
|
|
|
*
|
|
|
|
* @return uint16_t
|
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
uint16_t PMSBase::getRaw0_1(void) { return pms_raw0_1; }
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Read PMS 2.5 ug/m3 with CF = 1 PM estimates
|
|
|
|
*
|
|
|
|
* @return uint16_t
|
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
uint16_t PMSBase::getRaw2_5(void) { return pms_raw2_5; }
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Read PMS 10 ug/m3 with CF = 1 PM estimates
|
|
|
|
*
|
|
|
|
* @return uint16_t
|
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
uint16_t PMSBase::getRaw10(void) { return pms_raw10; }
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Read PMS 0.1 ug/m3
|
|
|
|
*
|
|
|
|
* @return uint16_t
|
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
uint16_t PMSBase::getPM0_1(void) { return pms_pm0_1; }
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Read PMS 2.5 ug/m3
|
|
|
|
*
|
|
|
|
* @return uint16_t
|
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
uint16_t PMSBase::getPM2_5(void) { return pms_pm2_5; }
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Read PMS 10 ug/m3
|
|
|
|
*
|
|
|
|
* @return uint16_t
|
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
uint16_t PMSBase::getPM10(void) { return pms_pm10; }
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Get numnber concentrations over 0.3 um/0.1L
|
|
|
|
*
|
|
|
|
* @return uint16_t
|
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
uint16_t PMSBase::getCount0_3(void) { return pms_count0_3; }
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Get numnber concentrations over 0.5 um/0.1L
|
|
|
|
*
|
|
|
|
* @return uint16_t
|
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
uint16_t PMSBase::getCount0_5(void) { return pms_count0_5; }
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Get numnber concentrations over 1.0 um/0.1L
|
|
|
|
*
|
|
|
|
* @return uint16_t
|
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
uint16_t PMSBase::getCount1_0(void) { return pms_count1_0; }
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Get numnber concentrations over 2.5 um/0.1L
|
|
|
|
*
|
|
|
|
* @return uint16_t
|
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
uint16_t PMSBase::getCount2_5(void) { return pms_count2_5; }
|
|
|
|
|
|
|
|
bool PMSBase::connected(void) { return _connected; }
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Get numnber concentrations over 5.0 um/0.1L (only PMS5003)
|
|
|
|
*
|
|
|
|
* @return uint16_t
|
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
uint16_t PMSBase::getCount5_0(void) { return pms_count5_0; }
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Get numnber concentrations over 10.0 um/0.1L (only PMS5003)
|
|
|
|
*
|
|
|
|
* @return uint16_t
|
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
uint16_t PMSBase::getCount10(void) { return pms_count10; }
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Get temperature (only PMS5003T)
|
|
|
|
*
|
|
|
|
* @return uint16_t
|
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
int16_t PMSBase::getTemp(void) { return pms_temp; }
|
2024-02-17 17:28:51 +07:00
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Get humidity (only PMS5003T)
|
|
|
|
*
|
|
|
|
* @return uint16_t
|
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
uint16_t PMSBase::getHum(void) { return pms_hum; }
|
2024-03-14 21:17:43 +07:00
|
|
|
|
2024-07-20 08:53:19 +07:00
|
|
|
/**
|
2024-08-07 08:50:43 +07:00
|
|
|
* @brief Get firmware version code
|
2024-09-21 17:48:18 +07:00
|
|
|
*
|
|
|
|
* @return uint8_t
|
2024-07-20 08:53:19 +07:00
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
uint8_t PMSBase::getFirmwareVersion(void) { return pms_firmwareVersion; }
|
2024-07-20 08:53:19 +07:00
|
|
|
|
2024-08-07 08:50:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Ge PMS5003 error code
|
2024-09-21 17:48:18 +07:00
|
|
|
*
|
|
|
|
* @return uint8_t
|
2024-08-07 08:50:43 +07:00
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
uint8_t PMSBase::getErrorCode(void) { return pms_errorCode; }
|
2024-08-07 08:50:43 +07:00
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Convert PMS2.5 to US AQI unit
|
|
|
|
*
|
|
|
|
* @param pm02
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
int PMSBase::pm25ToAQI(int pm02) {
|
2024-10-19 02:55:08 -05:00
|
|
|
if (pm02 <= 9.0)
|
|
|
|
return ((50 - 0) / (9.0 - .0) * (pm02 - .0) + 0);
|
2024-03-14 21:17:43 +07:00
|
|
|
else if (pm02 <= 35.4)
|
2024-10-19 02:55:08 -05:00
|
|
|
return ((100 - 51) / (35.4 - 9.1) * (pm02 - 9.0) + 51);
|
2024-03-14 21:17:43 +07:00
|
|
|
else if (pm02 <= 55.4)
|
2024-10-19 02:55:08 -05:00
|
|
|
return ((150 - 101) / (55.4 - 35.5) * (pm02 - 35.5) + 101);
|
|
|
|
else if (pm02 <= 125.4)
|
|
|
|
return ((200 - 151) / (125.4 - 55.5) * (pm02 - 55.5) + 151);
|
|
|
|
else if (pm02 <= 225.4)
|
|
|
|
return ((300 - 201) / (225.4 - 125.5) * (pm02 - 125.5) + 201);
|
|
|
|
else if (pm02 <= 325.4)
|
|
|
|
return ((500 - 301) / (325.4 - 225.5) * (pm02 - 225.5) + 301);
|
2024-03-14 21:17:43 +07:00
|
|
|
else
|
|
|
|
return 500;
|
|
|
|
}
|
|
|
|
|
2024-11-02 11:02:36 +07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief SLR correction for PM2.5
|
|
|
|
*
|
|
|
|
* Reference: https://www.airgradient.com/blog/low-readings-from-pms5003/
|
|
|
|
*
|
|
|
|
* @param pm25 PM2.5 raw value
|
|
|
|
* @param pm003Count PM0.3 count
|
|
|
|
* @param scalingFactor Scaling factor
|
|
|
|
* @param intercept Intercept
|
|
|
|
* @return float Calibrated PM2.5 value
|
|
|
|
*/
|
|
|
|
float PMSBase::slrCorrection(float pm25, float pm003Count, float scalingFactor, float intercept) {
|
|
|
|
float calibrated;
|
|
|
|
|
|
|
|
float lowCalibrated = (scalingFactor * pm003Count) + intercept;
|
|
|
|
if (lowCalibrated < 31) {
|
|
|
|
calibrated = lowCalibrated;
|
|
|
|
} else {
|
|
|
|
calibrated = pm25;
|
|
|
|
}
|
|
|
|
|
|
|
|
// No negative value for pm2.5
|
|
|
|
if (calibrated < 0) {
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return calibrated;
|
|
|
|
}
|
|
|
|
|
2024-07-21 07:13:34 +07:00
|
|
|
/**
|
|
|
|
* @brief Correction PM2.5
|
2024-09-21 17:48:18 +07:00
|
|
|
*
|
2024-09-01 19:56:11 +07:00
|
|
|
* Formula: https://www.airgradient.com/documentation/correction-algorithms/
|
2024-09-21 17:48:18 +07:00
|
|
|
*
|
2024-07-21 07:13:34 +07:00
|
|
|
* @param pm25 Raw PM2.5 value
|
|
|
|
* @param humidity Humidity value (%)
|
2024-10-21 01:49:01 +07:00
|
|
|
* @return compensated pm25 value
|
2024-07-21 07:13:34 +07:00
|
|
|
*/
|
2024-10-21 01:49:01 +07:00
|
|
|
float PMSBase::compensate(float pm25, float humidity) {
|
2024-07-21 07:13:34 +07:00
|
|
|
float value;
|
2024-10-21 01:49:01 +07:00
|
|
|
|
|
|
|
// Correct invalid humidity value
|
2024-07-21 07:13:34 +07:00
|
|
|
if (humidity < 0) {
|
|
|
|
humidity = 0;
|
|
|
|
}
|
|
|
|
if (humidity > 100) {
|
2024-08-26 20:43:48 +07:00
|
|
|
humidity = 100.0f;
|
2024-07-21 07:13:34 +07:00
|
|
|
}
|
|
|
|
|
2024-10-21 01:49:01 +07:00
|
|
|
// If its already 0, do not proceed
|
|
|
|
if (pm25 == 0) {
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pm25 < 30) { /** pm2.5 < 30 */
|
|
|
|
value = (pm25 * 0.524f) - (humidity * 0.0862f) + 5.75f;
|
|
|
|
} else if (pm25 < 50) { /** 30 <= pm2.5 < 50 */
|
|
|
|
value = (0.786f * (pm25 * 0.05f - 1.5f) + 0.524f * (1.0f - (pm25 * 0.05f - 1.5f))) * pm25 -
|
|
|
|
(0.0862f * humidity) + 5.75f;
|
|
|
|
} else if (pm25 < 210) { /** 50 <= pm2.5 < 210 */
|
|
|
|
value = (0.786f * pm25) - (0.0862f * humidity) + 5.75f;
|
|
|
|
} else if (pm25 < 260) { /** 210 <= pm2.5 < 260 */
|
|
|
|
value = (0.69f * (pm25 * 0.02f - 4.2f) + 0.786f * (1.0f - (pm25 * 0.02f - 4.2f))) * pm25 -
|
|
|
|
(0.0862f * humidity * (1.0f - (pm25 * 0.02f - 4.2f))) +
|
|
|
|
(2.966f * (pm25 * 0.02f - 4.2f)) + (5.75f * (1.0f - (pm25 * 0.02f - 4.2f))) +
|
|
|
|
(8.84f * (1.e-4) * pm25 * pm25 * (pm25 * 0.02f - 4.2f));
|
2024-08-26 20:43:48 +07:00
|
|
|
} else { /** 260 <= pm2.5 */
|
2024-10-21 01:49:01 +07:00
|
|
|
value = 2.966f + (0.69f * pm25) + (8.84f * (1.e-4) * pm25 * pm25);
|
2024-07-21 07:13:34 +07:00
|
|
|
}
|
|
|
|
|
2024-10-21 01:49:01 +07:00
|
|
|
// No negative value for pm2.5
|
2024-09-21 17:48:18 +07:00
|
|
|
if (value < 0) {
|
2024-10-21 01:49:01 +07:00
|
|
|
return 0.0;
|
2024-07-21 07:13:34 +07:00
|
|
|
}
|
|
|
|
|
2024-10-21 01:49:01 +07:00
|
|
|
return value;
|
2024-07-21 07:13:34 +07:00
|
|
|
}
|
|
|
|
|
2024-03-14 21:17:43 +07:00
|
|
|
/**
|
|
|
|
* @brief Convert two byte value to uint16_t value
|
|
|
|
*
|
|
|
|
* @param buf bytes array (must be >= 2)
|
2024-08-15 09:10:48 +07:00
|
|
|
* @return int16_t
|
2024-03-14 21:17:43 +07:00
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
int16_t PMSBase::toI16(const uint8_t *buf) {
|
2024-08-15 09:10:48 +07:00
|
|
|
int16_t value = buf[0];
|
|
|
|
value = (value << 8) | buf[1];
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2024-09-14 14:05:35 +07:00
|
|
|
uint16_t PMSBase::toU16(const uint8_t *buf) {
|
2024-08-15 09:10:48 +07:00
|
|
|
uint16_t value = buf[0];
|
|
|
|
value = (value << 8) | buf[1];
|
|
|
|
return value;
|
|
|
|
}
|
2024-03-14 21:17:43 +07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Validate package data
|
|
|
|
*
|
|
|
|
* @param buf Package buffer
|
|
|
|
* @return true Success
|
|
|
|
* @return false Failed
|
|
|
|
*/
|
2024-09-14 14:05:35 +07:00
|
|
|
bool PMSBase::validate(const uint8_t *buf) {
|
2024-03-14 21:17:43 +07:00
|
|
|
uint16_t sum = 0;
|
|
|
|
for (int i = 0; i < 30; i++) {
|
|
|
|
sum += buf[i];
|
2024-02-17 17:28:51 +07:00
|
|
|
}
|
2024-08-15 09:10:48 +07:00
|
|
|
if (sum == toU16(&buf[30])) {
|
2024-03-14 21:17:43 +07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2024-02-17 17:28:51 +07:00
|
|
|
}
|
2024-09-14 14:05:35 +07:00
|
|
|
|
|
|
|
void PMSBase::parse(const uint8_t *buf) {
|
2024-10-22 15:28:58 +07:00
|
|
|
// Standard particle
|
2024-09-14 14:05:35 +07:00
|
|
|
pms_raw0_1 = toU16(&buf[4]);
|
|
|
|
pms_raw2_5 = toU16(&buf[6]);
|
|
|
|
pms_raw10 = toU16(&buf[8]);
|
2024-10-22 15:28:58 +07:00
|
|
|
// atmospheric
|
2024-09-14 14:05:35 +07:00
|
|
|
pms_pm0_1 = toU16(&buf[10]);
|
|
|
|
pms_pm2_5 = toU16(&buf[12]);
|
|
|
|
pms_pm10 = toU16(&buf[14]);
|
2024-10-22 15:28:58 +07:00
|
|
|
|
|
|
|
// particle count
|
2024-09-14 14:05:35 +07:00
|
|
|
pms_count0_3 = toU16(&buf[16]);
|
|
|
|
pms_count0_5 = toU16(&buf[18]);
|
|
|
|
pms_count1_0 = toU16(&buf[20]);
|
|
|
|
pms_count2_5 = toU16(&buf[22]);
|
2024-10-22 15:28:58 +07:00
|
|
|
pms_count5_0 = toU16(&buf[24]); // PMS5003 only
|
|
|
|
pms_count10 = toU16(&buf[26]); // PMS5003 only
|
|
|
|
|
|
|
|
// Others
|
|
|
|
pms_temp = toU16(&buf[24]); // PMS5003T only
|
|
|
|
pms_hum = toU16(&buf[26]); // PMS5003T only
|
2024-09-14 14:05:35 +07:00
|
|
|
pms_firmwareVersion = buf[28];
|
|
|
|
pms_errorCode = buf[29];
|
|
|
|
}
|