diff --git a/I2CDEVICES.md b/I2CDEVICES.md index 75c0a1a9a..9eea5f2bc 100644 --- a/I2CDEVICES.md +++ b/I2CDEVICES.md @@ -67,3 +67,4 @@ Index | Define | Driver | Device | Address(es) | Description 43 | USE_AHT1x | xsns_63 | AHT10/15 | 0x38 | Temperature and humidity sensor 44 | USE_WEMOS_MOTOR_V1 | xdrv_34 | | 0x2D - 0x30 | WEMOS motor shield v1.0.0 (6612FNG) 45 | USE_HDC1080 | xsns_65 | HDC1080 | 0x40 | Temperature and Humidity sensor + 46 | USE_IAQ | xsns_66 | IAQ | 0x5a | Air quality sensor diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 1226b55fe..50c2387ad 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -499,6 +499,7 @@ // #define WEMOS_MOTOR_V1_ADDR 0x30 // Default I2C address 0x30 // #define WEMOS_MOTOR_V1_FREQ 1000 // Default frequency // #define USE_HDC1080 // [I2cDriver45] Enable HDC1080 temperature/humidity sensor (I2C address 0x40) (+1k5 code) + #define USE_IAQ // [I2cDriver46] Enable iAQ-core air quality sensor (I2C address 0x5a) (+0k6 code) // #define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino index 201711e48..7b094d534 100644 --- a/tasmota/support_features.ino +++ b/tasmota/support_features.ino @@ -542,8 +542,10 @@ void GetFeatures(void) #ifdef USE_HDC1080 feature6 |= 0x00000008; // xsns_65_hdc1080.ino #endif +#ifdef USE_IAQ + feature6 |= 0x00000010; // xsns_66_iAQ.ino +#endif -// feature6 |= 0x00000010; // feature6 |= 0x00000020; // feature6 |= 0x00000040; // feature6 |= 0x00000080; diff --git a/tasmota/xsns_66_iAQ.ino b/tasmota/xsns_66_iAQ.ino new file mode 100644 index 000000000..d115d0965 --- /dev/null +++ b/tasmota/xsns_66_iAQ.ino @@ -0,0 +1,137 @@ +/* + xsns_66_iAQ.ino - Support for iAQ-Core - Indoor Air Quality Sensor Module + + Copyright (C) 2020 Christian Baars and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_I2C +#ifdef USE_IAQ + +#define XSNS_66 66 +#define XI2C_46 46 // See I2CDEVICES.md + +#define I2_ADR_IAQ 0x5a // collides with MLX90614 and maybe others + +#define IAQ_STATUS_OK 0x00 +#define IAQ_STATUS_BUSY 0x01 +#define IAQ_STATUS_WARM 0x10 +#define IAQ_STATUS_ERR 0x80 +#define IAQ_STATUS_I2C_ERR 0xFF + +struct { + int32_t resistance; + uint16_t pred; + uint16_t Tvoc; + uint8_t status; + bool ready; +} iAQ; + +void IAQ_Init(void) +{ + if (!I2cSetDevice(I2_ADR_IAQ)) { return; } + I2cSetActiveFound(I2_ADR_IAQ, "IAQ"); + iAQ.ready = true; +} + +void IAQ_Read(void) +{ + uint8_t buf[9]; + buf[2] = IAQ_STATUS_I2C_ERR; // populate entry with error code + Wire.requestFrom((uint8_t)I2_ADR_IAQ,sizeof(buf)); + for( uint32_t i=0; i<9; i++ ) { + buf[i]= Wire.read(); + } + // AddLog_P2(LOG_LEVEL_DEBUG, "iAQ: buffer %x %x %x %x %x %x %x %x %x ", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8]); + iAQ.pred = (buf[0]<<8) + buf[1]; + iAQ.status = buf[2]; + iAQ.resistance = ((uint32_t)buf[3]<<24) + ((uint32_t)buf[4]<<16) + ((uint32_t)buf[5]<<8) + (uint32_t)buf[6]; + iAQ.Tvoc = (buf[7]<<8) + buf[8]; +} + +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +#ifdef USE_WEBSERVER +const char HTTP_SNS_IAQ[] PROGMEM = + "{s}iAQ-Core " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" // {s} = , {m} = , {e} = + "{s}iAQ-Core " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}"; + +const char HTTP_SNS_IAQ_ERROR[] PROGMEM = + "{s}iAQ-Core {m} %s {e}"; +#endif + +void IAQ_Show(uint8_t json) +{ + IAQ_Read(); + + if (json) { + if (iAQ.status!=IAQ_STATUS_OK){ + AddLog_P2(LOG_LEVEL_INFO, PSTR("iAQ: " D_ERROR " %x" ),iAQ.status); + return; + } + else { + ResponseAppend_P(PSTR(",\"IAQ\":{\"" D_JSON_ECO2 "\":%u,\"" D_JSON_TVOC "\":%u,\"" D_JSON_RESISTANCE "\":%u}"), iAQ.pred, iAQ.Tvoc, iAQ.resistance); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, iAQ.pred); +#endif // USE_DOMOTICZ + } +#ifdef USE_WEBSERVER + } else { + switch(iAQ.status){ + case IAQ_STATUS_OK: + WSContentSend_PD(HTTP_SNS_IAQ, iAQ.pred, iAQ.Tvoc); + break; + case IAQ_STATUS_WARM: + WSContentSend_PD(HTTP_SNS_IAQ_ERROR, D_START); + break; + default: + WSContentSend_PD(HTTP_SNS_IAQ_ERROR, D_ERROR); + } +#endif + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns66(byte function) +{ + if (!I2cEnabled(XI2C_46)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + IAQ_Init(); + } + else if (iAQ.ready) { + switch (function) { + case FUNC_JSON_APPEND: + IAQ_Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + IAQ_Show(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_IAQ +#endif // USE_I2C \ No newline at end of file diff --git a/tools/decode-status.py b/tools/decode-status.py index 25af64a97..d1d361d29 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -199,7 +199,7 @@ a_features = [[ "USE_AHT1x","USE_WEMOS_MOTOR_V1","USE_DEVICE_GROUPS","USE_PWM_DIMMER" ],[ "USE_KEELOQ","USE_HRXL","USE_SONOFF_D1","USE_HDC1080", - "","","","", + "USE_IAQ","","","", "","","","", "","","","", "","","","",