diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index e8eb00501..79b1693d2 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -4,12 +4,10 @@ * Change Wemo SetBinaryState to distinguish from GetBinaryState (#1357) * Change output of HTTP command to valid JSON only (#1363) * Change output to valid JSON Array if needed (#1363) + * Add support for sensor MH-Z19(B) to be enabled with define USE_MHZ19 in user_config.h (#561, #1248) * * 5.10.0a * Add (experimental) support for sensor SHT3x - * Add support for sensor MH-Z19(B) using serial interface to be enabled with define USE_MHZ19_HARD_SERIAL in user_config.h (#561, #1248) - * Add (experimental) support for sensor MH-Z19(B) using SoftwareSerial to be enabled with define USE_MHZ19_SOFT_SERIAL_OBSOLETE in user_config.h (#561, #1248) - * Add (experimental) support for sensor MH-Z19(B) using stripped SoftwareSerial to be enabled with define USE_MHZ19_SOFT_SERIAL in user_config.h (#561, #1248) * Add support for iTead SI7021 temperature and humidity sensor by consolidating DHT22 into AM2301 and using former DHT22 as SI7021 (#735) * Fix BME280 calculation (#1051) * Add support for BME680 using adafruit libraries (#1212) diff --git a/sonoff/user_config.h b/sonoff/user_config.h index bb73b3943..ebc248699 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -192,9 +192,7 @@ #define USE_WS2812_CTYPE 1 // WS2812 Color type (0 - RGB, 1 - GRB, 2 - RGBW, 3 - GRBW) // #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial RXD) (+1k mem). When USE_WS2812_DMA is enabled expect Exceptions on Pow -//#define USE_MHZ19_HARD_SERIAL // Add support for MH-Z19 CO2 sensor using hardware serial interface at 9600 bps on GPIO1/3 only (+1k1 code) -//#define USE_MHZ19_SOFT_SERIAL // Add support for MH-Z19 CO2 sensor using software serial interface at 9600 bps (+2k3 code, 215 iram) -//#define USE_MHZ19_SOFT_SERIAL_OBSOLETE // Add support for MH-Z19 CO2 sensor using software serial interface at 9600 bps (+2k3 code, 420 iram) +//#define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+1k8 code) #define USE_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code) diff --git a/sonoff/xsns_15_mhz_softserial.ino b/sonoff/xsns_15_mhz19.ino similarity index 74% rename from sonoff/xsns_15_mhz_softserial.ino rename to sonoff/xsns_15_mhz19.ino index 2e9c5ae0c..4eb35fbf3 100644 --- a/sonoff/xsns_15_mhz_softserial.ino +++ b/sonoff/xsns_15_mhz19.ino @@ -1,7 +1,7 @@ /* - xsns_15_mhz.ino - MH-Z19 CO2 sensor support for Sonoff-Tasmota + xsns_15_mhz.ino - MH-Z19(B) CO2 sensor support for Sonoff-Tasmota - Copyright (C) 2017 Theo Arends + Copyright (C) 2018 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 @@ -17,11 +17,11 @@ along with this program. If not, see . */ -#ifdef USE_MHZ19_SOFT_SERIAL +#ifdef USE_MHZ19 /*********************************************************************************************\ * MH-Z19 - CO2 sensor * - * Based on EspEasy plugin P049 by Dmitry (rel22 ___ inbox.ru) + * Adapted from EspEasy plugin P049 by Dmitry (rel22 ___ inbox.ru) ********************************************************************************************** * Filter usage * @@ -53,7 +53,7 @@ enum Mhz19FilterOptions {MHZ19_FILTER_OFF, MHZ19_FILTER_OFF_ALLSAMPLES, MHZ19_FI /*********************************************************************************************/ #define MHZ19_BAUDRATE 9600 -#define MHZ19_READ_TIMEOUT 600 // Must be way less than 1000 +#define MHZ19_READ_TIMEOUT 500 // Must be way less than 1000 const char kMhz19Types[] PROGMEM = "MHZ19|MHZ19B"; @@ -61,14 +61,17 @@ const uint8_t mhz19_cmnd_read_ppm[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0 const uint8_t mhz19_cmnd_abc_enable[9] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6}; const uint8_t mhz19_cmnd_abc_disable[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86}; -uint8_t mhz19_type = 0; +uint8_t mhz19_type = 1; uint16_t mhz19_last_ppm = 0; uint8_t mhz19_filter = MHZ19_FILTER_OPTION; -uint8_t mhz19_response[9]; bool mhz19_abc_enable = MHZ19_ABC_ENABLE; bool mhz19_abc_must_apply = false; char mhz19_types[7]; +float mhz19_temperature = 0; +uint8_t mhz19_timer = 0; +Ticker mhz19_ticker; + /*********************************************************************************************\ * Subset SoftwareSerial \*********************************************************************************************/ @@ -85,7 +88,7 @@ unsigned long mhz19_serial_bit_time; unsigned long mhz19_serial_bit_time_start; bool Mhz19SerialValidGpioPin(uint8_t pin) { - return (pin >= 0 && pin <= 5) || (pin >= 12 && pin <= 15); + return (pin >= 0 && pin <= 5) || (pin >= 9 && pin <= 10) || (pin >= 12 && pin <= 15); } bool Mhz19Serial(uint8_t receive_pin, uint8_t transmit_pin) @@ -158,7 +161,7 @@ size_t Mhz19SerialWrite(const uint8_t *buffer, size_t size = 1) { return n; } -void Mhz19SerialRxRead() ICACHE_RAM_ATTR; // Add 215 bytes to iram usage +//void Mhz19SerialRxRead() ICACHE_RAM_ATTR; // Add 215 bytes to iram usage void Mhz19SerialRxRead() { // Advance the starting point for the samples but compensate for the // initial delay which occurs before the interrupt is delivered @@ -222,31 +225,29 @@ bool Mhz19CheckAndApplyFilter(uint16_t ppm, uint8_t s) return true; } -bool Mhz19Read(uint16_t &p, float &t) +void Mhz19222ms() { - bool status = false; + uint8_t mhz19_response[9]; - p = 0; - t = NAN; + mhz19_timer++; + if (6 == mhz19_timer) { // MH-Z19 measuring cycle takes 1005 +5% ms + mhz19_timer = 0; - if (mhz19_type) - { Mhz19SerialFlush(); - if (Mhz19SerialWrite(mhz19_cmnd_read_ppm, 9) != 9) { - return false; // Unable to send 9 bytes - } - memset(mhz19_response, 0, sizeof(mhz19_response)); - uint32_t start = millis(); + Mhz19SerialWrite(mhz19_cmnd_read_ppm, 9); + } + + if (1 == mhz19_timer) { + unsigned long start = millis(); uint8_t counter = 0; while (((millis() - start) < MHZ19_READ_TIMEOUT) && (counter < 9)) { if (Mhz19SerialAvailable() > 0) { mhz19_response[counter++] = Mhz19SerialRead(); - } else { - delay(10); } } - if (counter < 9){ - return false; // Timeout while trying to read + if (counter < 9) { +// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 comms timeout")); + return; } byte crc = 0; @@ -255,61 +256,55 @@ bool Mhz19Read(uint16_t &p, float &t) } crc = 255 - crc; crc++; + if (mhz19_response[8] != crc) { +// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 crc error")); + return; + } + if (0xFF != mhz19_response[0] || 0x86 != mhz19_response[1]) { +// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 bad response")); + return; + } -/* - // Test data - mhz19_response[0] = 0xFF; - mhz19_response[1] = 0x86; - mhz19_response[2] = 0x12; - mhz19_response[3] = 0x86; - mhz19_response[4] = 64; -// mhz19_response[5] = 32; - mhz19_response[8] = crc; -*/ + uint16_t u = (mhz19_response[6] << 8) | mhz19_response[7]; + if (15000 == u) { // During (and only ever at) sensor boot, 'u' is reported as 15000 + if (!mhz19_abc_enable) { + // After bootup of the sensor the ABC will be enabled. + // Thus only actively disable after bootup. + mhz19_abc_must_apply = true; + } + } else { + uint16_t ppm = (mhz19_response[2] << 8) | mhz19_response[3]; + mhz19_temperature = ConvertTemp((float)mhz19_response[4] - 40); + uint8_t s = mhz19_response[5]; + mhz19_type = (s) ? 1 : 2; + if (Mhz19CheckAndApplyFilter(ppm, s)) { - if (0xFF == mhz19_response[0] && 0x86 == mhz19_response[1] && mhz19_response[8] == crc) { - uint16_t u = (mhz19_response[6] << 8) | mhz19_response[7]; - if (15000 == u) { // During (and only ever at) sensor boot, 'u' is reported as 15000 - if (!mhz19_abc_enable) { - // After bootup of the sensor the ABC will be enabled. - // Thus only actively disable after bootup. - mhz19_abc_must_apply = true; - } - } else { - uint16_t ppm = (mhz19_response[2] << 8) | mhz19_response[3]; - t = ConvertTemp((float)mhz19_response[4] - 40); - uint8_t s = mhz19_response[5]; - if (s) { - mhz19_type = 1; - } else { - mhz19_type = 2; - } - if (Mhz19CheckAndApplyFilter(ppm, s)) { - p = mhz19_last_ppm; - - if (0 == s || 64 == s) { // Reading is stable. - if (mhz19_abc_must_apply) { - mhz19_abc_must_apply = false; - if (mhz19_abc_enable) { - Mhz19SerialWrite(mhz19_cmnd_abc_enable, 9); // Sent sensor ABC Enable - } else { - Mhz19SerialWrite(mhz19_cmnd_abc_disable, 9); // Sent sensor ABC Disable - } + if (0 == s || 64 == s) { // Reading is stable. + if (mhz19_abc_must_apply) { + mhz19_abc_must_apply = false; + if (mhz19_abc_enable) { + Mhz19SerialWrite(mhz19_cmnd_abc_enable, 9); // Sent sensor ABC Enable + } else { + Mhz19SerialWrite(mhz19_cmnd_abc_disable, 9); // Sent sensor ABC Disable } } - - status = true; } + } } } - return status; } +/*********************************************************************************************/ + void Mhz19Init() { - if (Mhz19Serial(pin[GPIO_MHZ_RXD], pin[GPIO_MHZ_TXD])) { - mhz19_type = 1; + mhz19_type = 0; + if ((pin[GPIO_MHZ_RXD] < 99) && (pin[GPIO_MHZ_TXD] < 99)) { + if (Mhz19Serial(pin[GPIO_MHZ_RXD], pin[GPIO_MHZ_TXD])) { + mhz19_type = 1; + mhz19_ticker.attach_ms(222, Mhz19222ms); + } } } @@ -320,25 +315,20 @@ const char HTTP_SNS_CO2[] PROGMEM = void Mhz19Show(boolean json) { - uint16_t co2; - float t; + char temperature[10]; + dtostrfd(mhz19_temperature, Settings.flag2.temperature_resolution, temperature); + GetTextIndexed(mhz19_types, sizeof(mhz19_types), mhz19_type -1, kMhz19Types); - if (Mhz19Read(co2, t)) { - char temperature[10]; - dtostrfd(t, Settings.flag2.temperature_resolution, temperature); - GetTextIndexed(mhz19_types, sizeof(mhz19_types), mhz19_type -1, kMhz19Types); - - if (json) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_CO2 "\":%d,\"" D_TEMPERATURE "\":%s}"), mqtt_data, mhz19_types, co2, temperature); + if (json) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_CO2 "\":%d,\"" D_TEMPERATURE "\":%s}"), mqtt_data, mhz19_types, mhz19_last_ppm, temperature); #ifdef USE_DOMOTICZ - DomoticzSensor(DZ_COUNT, co2); + DomoticzSensor(DZ_COUNT, mhz19_last_ppm); #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER - } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_CO2, mqtt_data, mhz19_types, co2); - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, mhz19_types, temperature, TempUnit()); + } else { + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_CO2, mqtt_data, mhz19_types, mhz19_last_ppm); + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, mhz19_types, temperature, TempUnit()); #endif // USE_WEBSERVER - } } } @@ -352,21 +342,17 @@ boolean Xsns15(byte function) { boolean result = false; - if ((pin[GPIO_MHZ_RXD] < 99) && (pin[GPIO_MHZ_TXD] < 99)) { + if (mhz19_type) { switch (function) { case FUNC_XSNS_INIT: Mhz19Init(); break; - case FUNC_XSNS_PREP_BEFORE_TELEPERIOD: -// Mhz19Prep(); - break; case FUNC_XSNS_JSON_APPEND: Mhz19Show(1); break; #ifdef USE_WEBSERVER case FUNC_XSNS_WEB_APPEND: Mhz19Show(0); -// Mhz19Prep(); break; #endif // USE_WEBSERVER } @@ -374,4 +360,4 @@ boolean Xsns15(byte function) return result; } -#endif // USE_MHZ19_SOFT_SERIAL +#endif // USE_MHZ19 diff --git a/sonoff/xsns_15_mhz_hardserial.ino b/sonoff/xsns_15_mhz_hardserial.ino deleted file mode 100644 index 3f635436d..000000000 --- a/sonoff/xsns_15_mhz_hardserial.ino +++ /dev/null @@ -1,277 +0,0 @@ -/* - xsns_15_mhz.ino - MH-Z19 CO2 sensor support for Sonoff-Tasmota - - Copyright (C) 2017 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_MHZ19_HARD_SERIAL -/*********************************************************************************************\ - * MH-Z19 - CO2 sensor - * - * Supported on hardware serial interface only due to lack of iram needed by SoftwareSerial - * - * Based on EspEasy plugin P049 by Dmitry (rel22 ___ inbox.ru) - * - ********************************************************************************************** - * Filter usage - * - * Select filter usage on low stability readings -\*********************************************************************************************/ - -enum Mhz19FilterOptions {MHZ19_FILTER_OFF, MHZ19_FILTER_OFF_ALLSAMPLES, MHZ19_FILTER_FAST, MHZ19_FILTER_MEDIUM, MHZ19_FILTER_SLOW}; - -#define MHZ19_FILTER_OPTION MHZ19_FILTER_FAST - -/*********************************************************************************************\ - * Source: http://www.winsen-sensor.com/d/files/infrared-gas-sensor/mh-z19b-co2-ver1_0.pdf - * - * Automatic Baseline Correction (ABC logic function) - * - * ABC logic function refers to that sensor itself do zero point judgment and automatic calibration procedure - * intelligently after a continuous operation period. The automatic calibration cycle is every 24 hours after powered on. - * - * The zero point of automatic calibration is 400ppm. - * - * This function is usually suitable for indoor air quality monitor such as offices, schools and homes, - * not suitable for greenhouse, farm and refrigeratory where this function should be off. - * - * Please do zero calibration timely, such as manual or commend calibration. -\*********************************************************************************************/ - -#define MHZ19_ABC_ENABLE 1 // Automatic Baseline Correction (0 = off, 1 = on (default)) - -/*********************************************************************************************/ - -#define MHZ19_BAUDRATE 9600 -#define MHZ19_READ_TIMEOUT 600 // Must be way less than 1000 - -const char kMhz19Types[] PROGMEM = "MHZ19|MHZ19B"; - -const byte mhz19_cmnd_read_ppm[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; -const byte mhz19_cmnd_abc_enable[9] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6}; -const byte mhz19_cmnd_abc_disable[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86}; - -uint8_t mhz19_type = 0; -uint16_t mhz19_last_ppm = 0; -uint8_t mhz19_filter = MHZ19_FILTER_OPTION; -byte mhz19_response[9]; -bool mhz19_abc_enable = MHZ19_ABC_ENABLE; -bool mhz19_abc_must_apply = false; -char mhz19_types[7]; - -bool Mhz19CheckAndApplyFilter(uint16_t ppm, uint8_t s) -{ - if (1 == s) { - return false; // S==1 => "A" version sensor bootup, do not use values. - } - if (mhz19_last_ppm < 400 || mhz19_last_ppm > 5000) { - // Prevent unrealistic values during start-up with filtering enabled. - // Just assume the entered value is correct. - mhz19_last_ppm = ppm; - return true; - } - int32_t difference = ppm - mhz19_last_ppm; - if (s > 0 && s < 64 && mhz19_filter != MHZ19_FILTER_OFF) { - // Not the "B" version of the sensor, S value is used. - // S==0 => "B" version, else "A" version - // The S value is an indication of the stability of the reading. - // S == 64 represents a stable reading and any lower value indicates (unusual) fast change. - // Now we increase the delay filter for low values of S and increase response time when the - // value is more stable. - // This will make the reading useful in more turbulent environments, - // where the sensor would report more rapid change of measured values. - difference = difference * s; - difference /= 64; - } - switch (mhz19_filter) { - case MHZ19_FILTER_OFF: { - if (s != 0 && s != 64) { - return false; - } - break; - } - // #Samples to reach >= 75% of step response - case MHZ19_FILTER_OFF_ALLSAMPLES: - break; // No Delay - case MHZ19_FILTER_FAST: - difference /= 2; - break; // Delay: 2 samples - case MHZ19_FILTER_MEDIUM: - difference /= 4; - break; // Delay: 5 samples - case MHZ19_FILTER_SLOW: - difference /= 8; - break; // Delay: 11 samples - } - mhz19_last_ppm = static_cast(mhz19_last_ppm + difference); - return true; -} - -bool Mhz19Read(uint16_t &p, float &t) -{ - bool status = false; - - p = 0; - t = NAN; - - if (mhz19_type) - { - Serial.flush(); - if (Serial.write(mhz19_cmnd_read_ppm, 9) != 9) { - return false; // Unable to send 9 bytes - } - memset(mhz19_response, 0, sizeof(mhz19_response)); - uint32_t start = millis(); - uint8_t counter = 0; - while (((millis() - start) < MHZ19_READ_TIMEOUT) && (counter < 9)) { - if (Serial.available() > 0) { - mhz19_response[counter++] = Serial.read(); - } else { - delay(10); - } - } - if (counter < 9){ - return false; // Timeout while trying to read - } - - byte crc = 0; - for (uint8_t i = 1; i < 8; i++) { - crc += mhz19_response[i]; - } - crc = 255 - crc; - crc++; - -/* - // Test data - mhz19_response[0] = 0xFF; - mhz19_response[1] = 0x86; - mhz19_response[2] = 0x12; - mhz19_response[3] = 0x86; - mhz19_response[4] = 64; -// mhz19_response[5] = 32; - mhz19_response[8] = crc; -*/ - - if (0xFF == mhz19_response[0] && 0x86 == mhz19_response[1] && mhz19_response[8] == crc) { - uint16_t u = (mhz19_response[6] << 8) | mhz19_response[7]; - if (15000 == u) { // During (and only ever at) sensor boot, 'u' is reported as 15000 - if (!mhz19_abc_enable) { - // After bootup of the sensor the ABC will be enabled. - // Thus only actively disable after bootup. - mhz19_abc_must_apply = true; - } - } else { - uint16_t ppm = (mhz19_response[2] << 8) | mhz19_response[3]; - t = ConvertTemp((float)mhz19_response[4] - 40); - uint8_t s = mhz19_response[5]; - if (s) { - mhz19_type = 1; - } else { - mhz19_type = 2; - } - if (Mhz19CheckAndApplyFilter(ppm, s)) { - p = mhz19_last_ppm; - - if (0 == s || 64 == s) { // Reading is stable. - if (mhz19_abc_must_apply) { - mhz19_abc_must_apply = false; - if (mhz19_abc_enable) { - Serial.write(mhz19_cmnd_abc_enable, 9); // Sent sensor ABC Enable - } else { - Serial.write(mhz19_cmnd_abc_disable, 9); // Sent sensor ABC Disable - } - } - } - - status = true; - } - } - } - } - return status; -} - -void Mhz19Init() -{ - SetSerialBaudrate(MHZ19_BAUDRATE); - Serial.flush(); - - seriallog_level = 0; - mhz19_type = 1; -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_CO2[] PROGMEM = - "%s{s}%s " D_CO2 "{m}%d " D_UNIT_PPM "{e}"; // {s} = , {m} = , {e} = -#endif // USE_WEBSERVER - -void Mhz19Show(boolean json) -{ - uint16_t co2; - float t; - - if (Mhz19Read(co2, t)) { - char temperature[10]; - dtostrfd(t, Settings.flag2.temperature_resolution, temperature); - GetTextIndexed(mhz19_types, sizeof(mhz19_types), mhz19_type -1, kMhz19Types); - - if (json) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_CO2 "\":%d,\"" D_TEMPERATURE "\":%s}"), mqtt_data, mhz19_types, co2, temperature); -#ifdef USE_DOMOTICZ - DomoticzSensor(DZ_COUNT, co2); -#endif // USE_DOMOTICZ -#ifdef USE_WEBSERVER - } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_CO2, mqtt_data, mhz19_types, co2); - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, mhz19_types, temperature, TempUnit()); -#endif // USE_WEBSERVER - } - } -} - -/*********************************************************************************************\ - * Interface -\*********************************************************************************************/ - -#define XSNS_15 - -boolean Xsns15(byte function) -{ - boolean result = false; - - if ((pin[GPIO_MHZ_RXD] < 99) && (pin[GPIO_MHZ_TXD] < 99)) { - switch (function) { - case FUNC_XSNS_INIT: - Mhz19Init(); - break; - case FUNC_XSNS_PREP_BEFORE_TELEPERIOD: -// Mhz19Prep(); - break; - case FUNC_XSNS_JSON_APPEND: - Mhz19Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_XSNS_WEB_APPEND: - Mhz19Show(0); -// Mhz19Prep(); - break; -#endif // USE_WEBSERVER - } - } - return result; -} - -#endif // USE_MHZ19_HARD_SERIAL diff --git a/sonoff/xsns_15_mhz_softserial_obsolete.ino b/sonoff/xsns_15_mhz_softserial_obsolete.ino deleted file mode 100644 index be307e467..000000000 --- a/sonoff/xsns_15_mhz_softserial_obsolete.ino +++ /dev/null @@ -1,277 +0,0 @@ -/* - xsns_15_mhz.ino - MH-Z19 CO2 sensor support for Sonoff-Tasmota - - Copyright (C) 2017 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_MHZ19_SOFT_SERIAL_OBSOLETE -/*********************************************************************************************\ - * MH-Z19 - CO2 sensor - * - * Based on EspEasy plugin P049 by Dmitry (rel22 ___ inbox.ru) - ********************************************************************************************** - * Filter usage - * - * Select filter usage on low stability readings -\*********************************************************************************************/ - -#include -SoftwareSerial *SoftSerial; - -enum Mhz19FilterOptions {MHZ19_FILTER_OFF, MHZ19_FILTER_OFF_ALLSAMPLES, MHZ19_FILTER_FAST, MHZ19_FILTER_MEDIUM, MHZ19_FILTER_SLOW}; - -#define MHZ19_FILTER_OPTION MHZ19_FILTER_FAST - -/*********************************************************************************************\ - * Source: http://www.winsen-sensor.com/d/files/infrared-gas-sensor/mh-z19b-co2-ver1_0.pdf - * - * Automatic Baseline Correction (ABC logic function) - * - * ABC logic function refers to that sensor itself do zero point judgment and automatic calibration procedure - * intelligently after a continuous operation period. The automatic calibration cycle is every 24 hours after powered on. - * - * The zero point of automatic calibration is 400ppm. - * - * This function is usually suitable for indoor air quality monitor such as offices, schools and homes, - * not suitable for greenhouse, farm and refrigeratory where this function should be off. - * - * Please do zero calibration timely, such as manual or commend calibration. -\*********************************************************************************************/ - -#define MHZ19_ABC_ENABLE 1 // Automatic Baseline Correction (0 = off, 1 = on (default)) - -/*********************************************************************************************/ - -#define MHZ19_BAUDRATE 9600 -#define MHZ19_READ_TIMEOUT 600 // Must be way less than 1000 - -const char kMhz19Types[] PROGMEM = "MHZ19|MHZ19B"; - -const byte mhz19_cmnd_read_ppm[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; -const byte mhz19_cmnd_abc_enable[9] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6}; -const byte mhz19_cmnd_abc_disable[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86}; - -uint8_t mhz19_type = 0; -uint16_t mhz19_last_ppm = 0; -uint8_t mhz19_filter = MHZ19_FILTER_OPTION; -byte mhz19_response[9]; -bool mhz19_abc_enable = MHZ19_ABC_ENABLE; -bool mhz19_abc_must_apply = false; -char mhz19_types[7]; - -bool Mhz19CheckAndApplyFilter(uint16_t ppm, uint8_t s) -{ - if (1 == s) { - return false; // S==1 => "A" version sensor bootup, do not use values. - } - if (mhz19_last_ppm < 400 || mhz19_last_ppm > 5000) { - // Prevent unrealistic values during start-up with filtering enabled. - // Just assume the entered value is correct. - mhz19_last_ppm = ppm; - return true; - } - int32_t difference = ppm - mhz19_last_ppm; - if (s > 0 && s < 64 && mhz19_filter != MHZ19_FILTER_OFF) { - // Not the "B" version of the sensor, S value is used. - // S==0 => "B" version, else "A" version - // The S value is an indication of the stability of the reading. - // S == 64 represents a stable reading and any lower value indicates (unusual) fast change. - // Now we increase the delay filter for low values of S and increase response time when the - // value is more stable. - // This will make the reading useful in more turbulent environments, - // where the sensor would report more rapid change of measured values. - difference = difference * s; - difference /= 64; - } - switch (mhz19_filter) { - case MHZ19_FILTER_OFF: { - if (s != 0 && s != 64) { - return false; - } - break; - } - // #Samples to reach >= 75% of step response - case MHZ19_FILTER_OFF_ALLSAMPLES: - break; // No Delay - case MHZ19_FILTER_FAST: - difference /= 2; - break; // Delay: 2 samples - case MHZ19_FILTER_MEDIUM: - difference /= 4; - break; // Delay: 5 samples - case MHZ19_FILTER_SLOW: - difference /= 8; - break; // Delay: 11 samples - } - mhz19_last_ppm = static_cast(mhz19_last_ppm + difference); - return true; -} - -bool Mhz19Read(uint16_t &p, float &t) -{ - bool status = false; - - p = 0; - t = NAN; - - if (mhz19_type) - { - SoftSerial->flush(); - if (SoftSerial->write(mhz19_cmnd_read_ppm, 9) != 9) { - return false; // Unable to send 9 bytes - } - memset(mhz19_response, 0, sizeof(mhz19_response)); - uint32_t start = millis(); - uint8_t counter = 0; - while (((millis() - start) < MHZ19_READ_TIMEOUT) && (counter < 9)) { - if (SoftSerial->available() > 0) { - mhz19_response[counter++] = SoftSerial->read(); - } else { - delay(10); - } - } - if (counter < 9){ - return false; // Timeout while trying to read - } - - byte crc = 0; - for (uint8_t i = 1; i < 8; i++) { - crc += mhz19_response[i]; - } - crc = 255 - crc; - crc++; - -/* - // Test data - mhz19_response[0] = 0xFF; - mhz19_response[1] = 0x86; - mhz19_response[2] = 0x12; - mhz19_response[3] = 0x86; - mhz19_response[4] = 64; -// mhz19_response[5] = 32; - mhz19_response[8] = crc; -*/ - - if (0xFF == mhz19_response[0] && 0x86 == mhz19_response[1] && mhz19_response[8] == crc) { - uint16_t u = (mhz19_response[6] << 8) | mhz19_response[7]; - if (15000 == u) { // During (and only ever at) sensor boot, 'u' is reported as 15000 - if (!mhz19_abc_enable) { - // After bootup of the sensor the ABC will be enabled. - // Thus only actively disable after bootup. - mhz19_abc_must_apply = true; - } - } else { - uint16_t ppm = (mhz19_response[2] << 8) | mhz19_response[3]; - t = ConvertTemp((float)mhz19_response[4] - 40); - uint8_t s = mhz19_response[5]; - if (s) { - mhz19_type = 1; - } else { - mhz19_type = 2; - } - if (Mhz19CheckAndApplyFilter(ppm, s)) { - p = mhz19_last_ppm; - - if (0 == s || 64 == s) { // Reading is stable. - if (mhz19_abc_must_apply) { - mhz19_abc_must_apply = false; - if (mhz19_abc_enable) { - SoftSerial->write(mhz19_cmnd_abc_enable, 9); // Sent sensor ABC Enable - } else { - SoftSerial->write(mhz19_cmnd_abc_disable, 9); // Sent sensor ABC Disable - } - } - } - - status = true; - } - } - } - } - return status; -} - -void Mhz19Init() -{ - SoftSerial = new SoftwareSerial(pin[GPIO_MHZ_RXD], pin[GPIO_MHZ_TXD]); - SoftSerial->begin(9600); - - - mhz19_type = 1; -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_CO2[] PROGMEM = - "%s{s}%s " D_CO2 "{m}%d " D_UNIT_PPM "{e}"; // {s} = , {m} = , {e} = -#endif // USE_WEBSERVER - -void Mhz19Show(boolean json) -{ - uint16_t co2; - float t; - - if (Mhz19Read(co2, t)) { - char temperature[10]; - dtostrfd(t, Settings.flag2.temperature_resolution, temperature); - GetTextIndexed(mhz19_types, sizeof(mhz19_types), mhz19_type -1, kMhz19Types); - - if (json) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_CO2 "\":%d,\"" D_TEMPERATURE "\":%s}"), mqtt_data, mhz19_types, co2, temperature); -#ifdef USE_DOMOTICZ - DomoticzSensor(DZ_COUNT, co2); -#endif // USE_DOMOTICZ -#ifdef USE_WEBSERVER - } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_CO2, mqtt_data, mhz19_types, co2); - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, mhz19_types, temperature, TempUnit()); -#endif // USE_WEBSERVER - } - } -} - -/*********************************************************************************************\ - * Interface -\*********************************************************************************************/ - -#define XSNS_15 - -boolean Xsns15(byte function) -{ - boolean result = false; - - if ((pin[GPIO_MHZ_RXD] < 99) && (pin[GPIO_MHZ_TXD] < 99)) { - switch (function) { - case FUNC_XSNS_INIT: - Mhz19Init(); - break; - case FUNC_XSNS_PREP_BEFORE_TELEPERIOD: -// Mhz19Prep(); - break; - case FUNC_XSNS_JSON_APPEND: - Mhz19Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_XSNS_WEB_APPEND: - Mhz19Show(0); -// Mhz19Prep(); - break; -#endif // USE_WEBSERVER - } - } - return result; -} - -#endif // USE_MHZ19_SOFT_SERIAL_OBSOLETE