Skip to content

Commit 73cd5ce

Browse files
committed
Add support for US AQI and EPA AQI in PMS5003x sensors (#22294)
1 parent 16a145f commit 73cd5ce

File tree

4 files changed

+84
-4
lines changed

4 files changed

+84
-4
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file.
1515
- DALI inverted signal configuration using GPIO DALI RX_i/TX_i
1616
- Support for Shelly DALI Dimmer Gen3 (See tips and template in file xdrv_75_dali.ino)
1717
- HASPmota `haspmota.get_pages()` to get the sorted list of pages (#22358)
18+
- Support for US AQI and EPA AQI in PMS5003x sensors (#22294)
1819

1920
### Breaking Changed
2021

RELEASENOTES.md

+1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
122122
- DALI command `DaliTarget` to set light control broadcast, group number or gear number
123123
- DALI inverted signal configuration using GPIO DALI RX_i/TX_i
124124
- Support for Shelly DALI Dimmer Gen3 (See tips and template in file xdrv_75_dali.ino)
125+
- Support for US AQI and EPA AQI in PMS5003x sensors [#22294](https://github.com/arendst/Tasmota/issues/22294)
125126
- Mitsubishi Electric HVAC Operation time for MiElHVAC [#22334](https://github.com/arendst/Tasmota/issues/22334)
126127
- Mitsubishi Electric HVAC Outdoor Temperature for MiElHVAC [#22345](https://github.com/arendst/Tasmota/issues/22345)
127128
- Mitsubishi Electric HVAC Compressor Frequency for MiElHVAC [#22347](https://github.com/arendst/Tasmota/issues/22347)

tasmota/include/i18n.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -1006,7 +1006,9 @@ const char HTTP_SNS_STANDARD_CONCENTRATION[] PROGMEM = "{s}%s " D_STANDAR
10061006
const char HTTP_SNS_ENVIRONMENTAL_CONCENTRATION[] PROGMEM = "{s}%s " D_ENVIRONMENTAL_CONCENTRATION " %s " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}";
10071007
const char HTTP_SNS_F_ENVIRONMENTAL_CONCENTRATION[] PROGMEM = "{s}%s " D_ENVIRONMENTAL_CONCENTRATION " %s " D_UNIT_MICROMETER "{m}%1_f " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}";
10081008
const char HTTP_SNS_PARTICALS_BEYOND[] PROGMEM = "{s}%s " D_PARTICALS_BEYOND " %s " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}";
1009-
const char HTTP_SNS_AVG_RAD_DOSE[] PROGMEM = "{s}%s " D_AVG_RAD_DOSE " %s " D_UNIT_MINUTE "{m}%d.%02d " D_UNIT_US_H "{e}";
1009+
const char HTTP_SNS_AVG_RAD_DOSE[] PROGMEM = "{s}%s " D_AVG_RAD_DOSE " %s " D_UNIT_MINUTE "{m}%d.%02d " D_UNIT_US_H "{e}";
1010+
const char HTTP_SNS_US_AQI[] PROGMEM = "{s}%s US AQI" "{m}%d" "{e}";
1011+
const char HTTP_SNS_US_EPA_AQI[] PROGMEM = "{s}%s US EPA AQI" "{m}%d" "{e}";
10101012

10111013
const char HTTP_SNS_VOLTAGE[] PROGMEM = "{s}" D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}";
10121014
const char HTTP_SNS_CURRENT[] PROGMEM = "{s}" D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}";

tasmota/tasmota_xsns_sensor/xsns_18_pms5003.ino

+79-3
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,59 @@ void PmsInit(void) {
353353
}
354354
}
355355

356+
// This gives more accurate data for forest fire smoke. PurpleAir gives you this conversion option labeled "US EPA"
357+
// https://cfpub.epa.gov/si/si_public_record_report.cfm?dirEntryId=353088&Lab=CEMM
358+
/*
359+
Copy-paste from the PDF Slide 26
360+
y={0 ≤ x <30: 0.524*x - 0.0862*RH + 5.75}
361+
y={30≤ x <50: (0.786*(x/20 - 3/2) + 0.524*(1 - (x/20 - 3/2)))*x -0.0862*RH + 5.75}
362+
y={50 ≤ x <210: 0.786*x - 0.0862*RH + 5.75}
363+
y={210 ≤ x <260: (0.69*(x/50 – 21/5) + 0.786*(1 - (x/50 – 21/5)))*x - 0.0862*RH*(1 - (x/50 – 21/5)) + 2.966*(x/50 – 21/5) + 5.75*(1 - (x/50 – 21/5)) + 8.84*(10^{-4})*x^{2}*(x/50 – 21/5)}
364+
y={260 ≤ x: 2.966 + 0.69*x + 8.84*10^{-4}*x^2}
365+
366+
y= corrected PM2.5 µg/m3
367+
x= PM2.5 cf_atm (lower)
368+
RH= Relative humidity as measured by the PurpleAir
369+
*/
370+
int usaEpaStandardPm2d5Adjustment(int pm25_standard, int relative_humidity)
371+
{
372+
// Rename to use the same variables from the paper
373+
float x = pm25_standard;
374+
float RH = relative_humidity;
375+
if (x<30) {
376+
return 0.524f * x - 0.0862f * RH + 5.75f;
377+
} else if(x<50) {
378+
return (0.786f * (x/20.0f - 3.0f/2.0f) + 0.524f * (1.0f - (x/20.0f - 3.0f/2.0f))) * x - 0.0862f * RH + 5.75f;
379+
} else if(x<210) {
380+
return 0.786f * x - 0.0862f * RH + 5.75f;
381+
} else if(x<260) {
382+
return (0.69f * (x/50.0f - 21.0f/5.0f) + 0.786f * (1.0f - (x/50.0f - 21.0f/5.0f))) * x - 0.0862f * RH * (1.0f - (x/50.0f - 21.0f/5.0f)) + 2.966f * (x/50.0f - 21.0f/5.0f) + 5.75f * (1.0f - (x/50.0f - 21.0f/5.0f)) + 8.84f * FastPrecisePowf(10.0f, -4.0f) * FastPrecisePowf(x,2.0f) * (x/50.0f - 21.0f/5.0f);
383+
} else {
384+
return 2.966f + 0.69f * x + 8.84f * FastPrecisePowf(10.0f, -4.0f) * FastPrecisePowf(x, 2.0f);
385+
}
386+
}
387+
388+
// Compute US AQI using the 2024+ table
389+
// https://forum.airnowtech.org/t/the-aqi-equation-2024-valid-beginning-may-6th-2024/453
390+
int compute_us_aqi(int pm25_standard)
391+
{
392+
if (pm25_standard <= 9) {
393+
return map_double(pm25_standard, 0, 9, 0, 50);
394+
} else if (pm25_standard <= 35) {
395+
return map_double(pm25_standard, 9.1f, 35.4f, 51, 100);
396+
} else if (pm25_standard <= 55) {
397+
return map_double(pm25_standard, 35.5f, 55.4f, 101, 150);
398+
} else if (pm25_standard <= 125) {
399+
return map_double(pm25_standard, 55.5f, 125.4f, 151, 200);
400+
} else if (pm25_standard <= 225) {
401+
return map_double(pm25_standard, 125.5f, 225.4f, 201, 300);
402+
} else if (pm25_standard <= 325) {
403+
return map_double(pm25_standard, 225.5f, 325.4f, 301, 500);
404+
} else {
405+
return 500;
406+
}
407+
}
408+
356409
void PmsShow(bool json) {
357410
if (Pms.valid) {
358411
char types[10];
@@ -370,20 +423,37 @@ void PmsShow(bool json) {
370423
#ifdef PMS_MODEL_PMS5003T
371424
float temperature = ConvertTemp(pms_data.temperature10x/10.0);
372425
float humidity = ConvertHumidity(pms_data.humidity10x/10.0);
426+
int epa_us_aqi;
427+
// When in Fahrenheit include US AQI
428+
if (Settings->flag.temperature_conversion) { // Fahrenheit - US, Liberia, Cayman Islands
429+
epa_us_aqi = compute_us_aqi(usaEpaStandardPm2d5Adjustment(pms_data.pm25_standard, humidity));
430+
}
373431
#endif // PMS_MODEL_PMS5003T
432+
int us_aqi;
433+
// Use US AQI for Fahrenheit, EAQI (European Air Quality Index) for Celsius
434+
if (Settings->flag.temperature_conversion) { // Fahrenheit - US, Liberia, Cayman Islands
435+
us_aqi = compute_us_aqi(pms_data.pm25_standard);
436+
}
374437

375438
if (json) {
376439
ResponseAppend_P(PSTR(",\"%s\":{\"CF1\":%d,\"CF2.5\":%d,\"CF10\":%d,\"PM1\":%d,\"PM2.5\":%d,\"PM10\":%d"),
377440
types,
378441
pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard,
379442
pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env);
443+
if (Settings->flag.temperature_conversion) { // Fahrenheit - US, Liberia, Cayman Islands
444+
ResponseAppend_P(PSTR(",\"US_AQI\":%d"), us_aqi);
445+
}
380446
#if !(defined(PMS_MODEL_PMS3003) || defined(PMS_MODEL_ZH03X))
381-
ResponseAppend_P(PSTR(",\"PB0.3\":%d,\"PB0.5\":%d,\"PB1\":%d,\"PB2.5\":%d,"),
447+
ResponseAppend_P(PSTR(",\"PB0.3\":%d,\"PB0.5\":%d,\"PB1\":%d,\"PB2.5\":%d"),
382448
pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um);
383449
#ifdef PMS_MODEL_PMS5003T
450+
ResponseAppend_P(PSTR(","));
384451
ResponseAppendTHD(temperature, humidity);
452+
if (Settings->flag.temperature_conversion) { // Fahrenheit - US, Liberia, Cayman Islands
453+
ResponseAppend_P(PSTR(",\"US_EPA_AQI\":%d"), epa_us_aqi);
454+
}
385455
#else
386-
ResponseAppend_P(PSTR("\"PB5\":%d,\"PB10\":%d"),
456+
ResponseAppend_P(PSTR(",\"PB5\":%d,\"PB10\":%d"),
387457
pms_data.particles_50um, pms_data.particles_100um);
388458
#endif // PMS_MODEL_PMS5003T
389459
#endif // No PMS_MODEL_PMS3003
@@ -409,8 +479,14 @@ void PmsShow(bool json) {
409479
WSContentSend_PD(HTTP_SNS_PARTICALS_BEYOND, types, "0.5", pms_data.particles_05um);
410480
WSContentSend_PD(HTTP_SNS_PARTICALS_BEYOND, types, "1", pms_data.particles_10um);
411481
WSContentSend_PD(HTTP_SNS_PARTICALS_BEYOND, types, "2.5", pms_data.particles_25um);
482+
if (Settings->flag.temperature_conversion) { // Fahrenheit - US, Liberia, Cayman Islands
483+
WSContentSend_PD(HTTP_SNS_US_AQI, types, us_aqi);
484+
}
412485
#ifdef PMS_MODEL_PMS5003T
413486
WSContentSend_THD(types, temperature, humidity);
487+
if (Settings->flag.temperature_conversion) { // Fahrenheit - US, Liberia, Cayman Islands
488+
WSContentSend_PD(HTTP_SNS_US_EPA_AQI, types, epa_us_aqi);
489+
}
414490
#else
415491
WSContentSend_PD(HTTP_SNS_PARTICALS_BEYOND, types, "5", pms_data.particles_50um);
416492
WSContentSend_PD(HTTP_SNS_PARTICALS_BEYOND, types, "10", pms_data.particles_100um);
@@ -455,4 +531,4 @@ bool Xsns18(uint32_t function)
455531
return result;
456532
}
457533

458-
#endif // USE_PMS5003
534+
#endif // USE_PMS5003

0 commit comments

Comments
 (0)