diff --git a/README.md b/README.md index 3feb7fc5b..c2f88067f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## Sonoff-Tasmota Provide ESP8266 based Sonoff by [iTead Studio](https://www.itead.cc/) and ElectroDragon IoT Relay with Serial, Web and MQTT control allowing 'Over the Air' or OTA firmware updates using Arduino IDE. -Current version is **5.10.0b** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for change information. +Current version is **5.10.0c** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for change information. ### ATTENTION All versions diff --git a/lib/TasmotaSerial-1.0.0/README.md b/lib/TasmotaSerial-1.0.0/README.md new file mode 100644 index 000000000..5c7a24721 --- /dev/null +++ b/lib/TasmotaSerial-1.0.0/README.md @@ -0,0 +1,8 @@ +# TasmotaSerial + +Implementation of software serial library for the ESP8266 at 9600 baud + +Allows for several instances to be active at the same time. + +Please note that due to the fact that the ESP always have other activities ongoing, there will be some inexactness in interrupt +timings. This may lead to bit errors when having heavy data traffic. diff --git a/lib/TasmotaSerial-1.0.0/examples/swsertest/swsertest.ino b/lib/TasmotaSerial-1.0.0/examples/swsertest/swsertest.ino new file mode 100644 index 000000000..9e9e30179 --- /dev/null +++ b/lib/TasmotaSerial-1.0.0/examples/swsertest/swsertest.ino @@ -0,0 +1,27 @@ + +#include + +TasmotaSerial swSer(14, 12); + +void setup() { + Serial.begin(115200); + swSer.begin(); + + Serial.println("\nTasmota serial test started"); + + for (char ch = ' '; ch <= 'z'; ch++) { + swSer.write(ch); + } + swSer.println(""); + +} + +void loop() { + while (swSer.available() > 0) { + Serial.write(swSer.read()); + } + while (Serial.available() > 0) { + swSer.write(Serial.read()); + } + +} diff --git a/lib/TasmotaSerial-1.0.0/keywords.txt b/lib/TasmotaSerial-1.0.0/keywords.txt new file mode 100644 index 000000000..87974971e --- /dev/null +++ b/lib/TasmotaSerial-1.0.0/keywords.txt @@ -0,0 +1,26 @@ +####################################### +# Syntax Coloring Map for TasmotaSerial +# (esp8266) +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +TasmotaSerial KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +read KEYWORD2 +write KEYWORD2 +available KEYWORD2 +flush KEYWORD2 +peek KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + diff --git a/lib/TasmotaSerial-1.0.0/library.json b/lib/TasmotaSerial-1.0.0/library.json new file mode 100644 index 000000000..986c6d7ac --- /dev/null +++ b/lib/TasmotaSerial-1.0.0/library.json @@ -0,0 +1,15 @@ +{ + "name": "TasmotaSerial", + "version": "1.0.0", + "keywords": [ + "serial", "io", "TasmotaSerial" + ], + "description": "Implementation of software serial for ESP8266 at 9600 baud.", + "repository": + { + "type": "git", + "url": "https://github.com/arendst/Sonoff-Tasmota/lib/TasmotaSerial" + }, + "frameworks": "arduino", + "platforms": "espressif8266" +} diff --git a/lib/TasmotaSerial-1.0.0/library.properties b/lib/TasmotaSerial-1.0.0/library.properties new file mode 100644 index 000000000..703b613ee --- /dev/null +++ b/lib/TasmotaSerial-1.0.0/library.properties @@ -0,0 +1,9 @@ +name=TasmotaSerial +version=1.0 +author=Theo Arends +maintainer=Theo Arends +sentence=Implementation of software serial for ESP8266 at 9600 baud. +paragraph= +category=Signal Input/Output +url= +architectures=esp8266 diff --git a/lib/TasmotaSerial-1.0.0/src/TasmotaSerial.cpp b/lib/TasmotaSerial-1.0.0/src/TasmotaSerial.cpp new file mode 100644 index 000000000..1b55818e0 --- /dev/null +++ b/lib/TasmotaSerial-1.0.0/src/TasmotaSerial.cpp @@ -0,0 +1,193 @@ +/* + TasmotaSerial.cpp - Minimal implementation of software serial for Tasmota + + Copyright (C) 2018 Theo Arends + + This library 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 . +*/ + +#include + +// The Arduino standard GPIO routines are not enough, +// must use some from the Espressif SDK as well +extern "C" { +#include "gpio.h" +} + +#include + +// As the Arduino attachInterrupt has no parameter, lists of objects +// and callbacks corresponding to each possible GPIO pins have to be defined +TasmotaSerial *ObjList[16]; + +#ifdef TM_SERIAL_USE_IRAM +void ICACHE_RAM_ATTR sws_isr_0() { ObjList[0]->rxRead(); }; +void ICACHE_RAM_ATTR sws_isr_1() { ObjList[1]->rxRead(); }; +void ICACHE_RAM_ATTR sws_isr_2() { ObjList[2]->rxRead(); }; +void ICACHE_RAM_ATTR sws_isr_3() { ObjList[3]->rxRead(); }; +void ICACHE_RAM_ATTR sws_isr_4() { ObjList[4]->rxRead(); }; +void ICACHE_RAM_ATTR sws_isr_5() { ObjList[5]->rxRead(); }; +// Pin 6 to 11 can not be used +void ICACHE_RAM_ATTR sws_isr_12() { ObjList[12]->rxRead(); }; +void ICACHE_RAM_ATTR sws_isr_13() { ObjList[13]->rxRead(); }; +void ICACHE_RAM_ATTR sws_isr_14() { ObjList[14]->rxRead(); }; +void ICACHE_RAM_ATTR sws_isr_15() { ObjList[15]->rxRead(); }; +#else +void sws_isr_0() { ObjList[0]->rxRead(); }; +void sws_isr_1() { ObjList[1]->rxRead(); }; +void sws_isr_2() { ObjList[2]->rxRead(); }; +void sws_isr_3() { ObjList[3]->rxRead(); }; +void sws_isr_4() { ObjList[4]->rxRead(); }; +void sws_isr_5() { ObjList[5]->rxRead(); }; +// Pin 6 to 11 can not be used +void sws_isr_12() { ObjList[12]->rxRead(); }; +void sws_isr_13() { ObjList[13]->rxRead(); }; +void sws_isr_14() { ObjList[14]->rxRead(); }; +void sws_isr_15() { ObjList[15]->rxRead(); }; +#endif // TM_SERIAL_USE_IRAM + +static void (*ISRList[16])() = { + sws_isr_0, + sws_isr_1, + sws_isr_2, + sws_isr_3, + sws_isr_4, + sws_isr_5, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + sws_isr_12, + sws_isr_13, + sws_isr_14, + sws_isr_15 +}; + +TasmotaSerial::TasmotaSerial(int receive_pin, int transmit_pin) +{ + m_valid = false; + if (!((isValidGPIOpin(receive_pin)) && (isValidGPIOpin(transmit_pin) || transmit_pin == 16))) { + return; + } + m_buffer = (uint8_t*)malloc(TM_SERIAL_BUFFER_SIZE); + if (m_buffer == NULL) { + return; + } + m_valid = true; + m_rx_pin = receive_pin; + m_tx_pin = transmit_pin; + m_in_pos = m_out_pos = 0; + // Use getCycleCount() loop to get as exact timing as possible + m_bit_time = ESP.getCpuFreqMHz() *1000000 /TM_SERIAL_BAUDRATE; + pinMode(m_rx_pin, INPUT); + ObjList[m_rx_pin] = this; + attachInterrupt(m_rx_pin, ISRList[m_rx_pin], FALLING); + pinMode(m_tx_pin, OUTPUT); + digitalWrite(m_tx_pin, HIGH); +} + +bool TasmotaSerial::isValidGPIOpin(int pin) +{ + return (pin >= 0 && pin <= 5) || (pin >= 12 && pin <= 15); +} + +bool TasmotaSerial::begin() { + return m_valid; +} + +int TasmotaSerial::read() +{ + if (m_in_pos == m_out_pos) { + return -1; + } + uint8_t ch = m_buffer[m_out_pos]; + m_out_pos = (m_out_pos +1) % TM_SERIAL_BUFFER_SIZE; + return ch; +} + +int TasmotaSerial::available() +{ + int avail = m_in_pos - m_out_pos; + if (avail < 0) { + avail += TM_SERIAL_BUFFER_SIZE; + } + return avail; +} + +//#define TM_SERIAL_WAIT { while (ESP.getCycleCount()-start < wait) optimistic_yield(1); wait += m_bit_time; } // Watchdog timeouts +#define TM_SERIAL_WAIT { while (ESP.getCycleCount()-start < wait); wait += m_bit_time; } + +size_t TasmotaSerial::txWrite(uint8_t b) +{ + unsigned long wait = m_bit_time; + digitalWrite(m_tx_pin, HIGH); + unsigned long start = ESP.getCycleCount(); + // Start bit; + digitalWrite(m_tx_pin, LOW); + TM_SERIAL_WAIT; + for (int i = 0; i < 8; i++) { + digitalWrite(m_tx_pin, (b & 1) ? HIGH : LOW); + TM_SERIAL_WAIT; + b >>= 1; + } + // Stop bit + digitalWrite(m_tx_pin, HIGH); + TM_SERIAL_WAIT; + return 1; +} + +size_t TasmotaSerial::write(const uint8_t *buffer, size_t size) +{ + size_t n = 0; + // Flush input buffer on every write + m_in_pos = m_out_pos = 0; + while(size--) { + n += txWrite(*buffer++); + } + return n; +} + +#ifdef TM_SERIAL_USE_IRAM +void ICACHE_RAM_ATTR TasmotaSerial::rxRead() +{ +#else +void TasmotaSerial::rxRead() +{ +#endif + // Advance the starting point for the samples but compensate for the + // initial delay which occurs before the interrupt is delivered + unsigned long wait = m_bit_time + m_bit_time/3 - 500; + unsigned long start = ESP.getCycleCount(); + uint8_t rec = 0; + for (int i = 0; i < 8; i++) { + TM_SERIAL_WAIT; + rec >>= 1; + if (digitalRead(m_rx_pin)) { + rec |= 0x80; + } + } + // Stop bit + TM_SERIAL_WAIT; + // Store the received value in the buffer unless we have an overflow + int next = (m_in_pos+1) % TM_SERIAL_BUFFER_SIZE; + if (next != m_out_pos) { + m_buffer[m_in_pos] = rec; + m_in_pos = next; + } + // Must clear this bit in the interrupt register, + // it gets set even when interrupts are disabled + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << m_rx_pin); +} diff --git a/lib/TasmotaSerial-1.0.0/src/TasmotaSerial.h b/lib/TasmotaSerial-1.0.0/src/TasmotaSerial.h new file mode 100644 index 000000000..beada7792 --- /dev/null +++ b/lib/TasmotaSerial-1.0.0/src/TasmotaSerial.h @@ -0,0 +1,56 @@ +/* + TasmotaSerial.h - Minimal implementation of software serial for Tasmota + + Copyright (C) 2018 Theo Arends + + This library 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 . +*/ + +#ifndef TasmotaSerial_h +#define TasmotaSerial_h +/*********************************************************************************************\ + * TasmotaSerial supports 9600 baud with fixed buffer size of 20 bytes using optional no iram + * + * Based on EspSoftwareSerial v3.3.1 by Peter Lerup (https://github.com/plerup/espsoftwareserial) +\*********************************************************************************************/ + +#define TM_SERIAL_BAUDRATE 9600 +#define TM_SERIAL_BUFFER_SIZE 20 +//#define TM_SERIAL_USE_IRAM // Enable to use iram (+368 bytes) + +class TasmotaSerial { + public: + TasmotaSerial(int receive_pin, int transmit_pin); + bool begin(); + size_t write(const uint8_t *buffer, size_t size = 1); + int read(); + int available(); + + void rxRead(); + + private: + bool isValidGPIOpin(int pin); + size_t txWrite(uint8_t byte); + + // Member variables + bool m_valid; + int m_rx_pin; + int m_tx_pin; + unsigned long m_bit_time; + unsigned int m_in_pos; + unsigned int m_out_pos; + uint8_t *m_buffer; +}; + +#endif // TasmotaSerial_h diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index 826c4011b..bab94bd58 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,14 +1,20 @@ -/* 5.10.0b - * Add optional support for PZEM004T energy sensor - * Change Sonoff Pow Enenrgy MQTT data message and consolidate Status 8 into Status 10 +/* 5.10.0c + * Consolidate device serial (MH-Z19, SenseAir and Pzem004T) into TasmotaSerial library + * Consolidate PWM device recognition + * Fix Wemo Emulation (#1357) + * Add support for Arilux LC06 (#1414) + * + * 5.10.0b + * Add support for PZEM004T energy sensor to be enabled with define USE_PZEM004T in user_config.h + * Change Sonoff Pow Energy MQTT data message and consolidate Status 8 into Status 10 * Change Wemo SetBinaryState to distinguish from GetBinaryState (#1357) - * Change output of HTTP command to valid JSON only (#1363) + * Change output of HTTP command to valid JSON and Array only (#1363) + * Add support for MH-Z19(B) CO2 sensor to be enabled with define USE_MHZ19 in user_config.h (#561, #1248) + * Add support for SenseAir S8 CO2 sensor to be enabled with define USE_SENSEAIR in user_config.h + * Add support for Domoticz Air Quality sensor to be used by MH-Z19(B) and SenseAir sensors * * 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/i18n.h b/sonoff/i18n.h index f7bebbbf8..3c3fd66be 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -1,7 +1,7 @@ /* i18n.h - internationalization 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 @@ -121,6 +121,10 @@ const char HTTP_SNS_PRESSURE[] PROGMEM = "%s{s}%s " D_PRESSURE "{m}%s " D_UNIT_P const char HTTP_SNS_SEAPRESSURE[] PROGMEM = "%s{s}%s " D_PRESSUREATSEALEVEL "{m}%s " D_UNIT_PRESSURE "{e}"; // {s} = , {m} = , {e} = const char HTTP_SNS_ANALOG[] PROGMEM = "%s{s}%s " D_ANALOG_INPUT "%d{m}%d{e}"; // {s} = , {m} = , {e} = +#if defined(USE_MHZ19) || defined(USE_SENSEAIR) +const char HTTP_SNS_CO2[] PROGMEM = "%s{s}%s " D_CO2 "{m}%d " D_UNIT_PPM "{e}"; // {s} = , {m} = , {e} = +#endif // USE_WEBSERVER + const char S_MAIN_MENU[] PROGMEM = D_MAIN_MENU; const char S_CONFIGURATION[] PROGMEM = D_CONFIGURATION; const char S_CONFIGURE_MODULE[] PROGMEM = D_CONFIGURE_MODULE; diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index 45b9d0c55..bbe053a20 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -1,7 +1,7 @@ /* de-DE.h - localization for German - Germany for Sonoff-Tasmota - Copyright (C) 2017 VinceMasuka + Copyright (C) 2018 VinceMasuka 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 @@ -66,7 +66,7 @@ #define D_BUTTON "Knopf" #define D_BY "von" // Written by me #define D_CELSIUS "Celsius" -#define D_CO2 "CO2" +#define D_CO2 "Kohlenstoffdioxid" #define D_CODE "code" // Button code #define D_COLDLIGHT "kalt" #define D_COMMAND "Befehl" @@ -118,6 +118,7 @@ #define D_INITIALIZED "initialisiert" #define D_IP_ADDRESS "IP-Addresse" #define D_LIGHT "Licht" +#define D_LIMIT "Grenzwert" #define D_LOCAL_TIME "lokale Zeit" #define D_LOW "niedrig" #define D_LWT "LWT" @@ -386,6 +387,7 @@ #define D_DOMOTICZ_COUNT "Count" #define D_DOMOTICZ_VOLTAGE "Voltage" #define D_DOMOTICZ_CURRENT "Current" + #define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_UPDATE_TIMER "Update timer" // xdrv_irremote.ino @@ -473,12 +475,14 @@ #define D_SENSOR_RELAY "Relay " // Suffix "1i" #define D_SENSOR_LED "LED " // Suffix "1i" #define D_SENSOR_PWM "PWM " // Suffix "1" -#define D_SENSOR_COUNTER "Counter" // Suffix "1" +#define D_SENSOR_COUNTER "Counter" // Suffix "1" #define D_SENSOR_IRRECV "IRRecv" #define D_SENSOR_MHZ_RX "MHZ Rx" #define D_SENSOR_MHZ_TX "MHZ Tx" #define D_SENSOR_PZEM_RX "PZEM Rx" #define D_SENSOR_PZEM_TX "PZEM Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" #define D_SENSOR_BACKLIGHT "BLight" diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index 571fff023..497654872 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -1,7 +1,7 @@ /* en-GB.h - localization for English - United Kingdom 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 @@ -66,7 +66,7 @@ #define D_BUTTON "Button" #define D_BY "by" // Written by me #define D_CELSIUS "Celsius" -#define D_CO2 "CO2" +#define D_CO2 "Carbon dioxide" #define D_CODE "code" // Button code #define D_COLDLIGHT "Cold" #define D_COMMAND "Command" @@ -118,6 +118,7 @@ #define D_INITIALIZED "Initialized" #define D_IP_ADDRESS "IP Address" #define D_LIGHT "Light" +#define D_LIMIT "Limit" #define D_LOCAL_TIME "Local" #define D_LOW "Low" #define D_LWT "LWT" @@ -386,6 +387,7 @@ #define D_DOMOTICZ_COUNT "Count" #define D_DOMOTICZ_VOLTAGE "Voltage" #define D_DOMOTICZ_CURRENT "Current" + #define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_UPDATE_TIMER "Update timer" // xdrv_irremote.ino @@ -479,6 +481,8 @@ #define D_SENSOR_MHZ_TX "MHZ Tx" #define D_SENSOR_PZEM_RX "PZEM Rx" #define D_SENSOR_PZEM_TX "PZEM Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" #define D_SENSOR_BACKLIGHT "BLight" diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index 13b1b49cb..a6f3b020e 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -1,7 +1,7 @@ /* nl-NL.h - localization for Dutch - Nederland 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 @@ -66,7 +66,7 @@ #define D_BUTTON "DrukKnop" #define D_BY "door" // Written by me #define D_CELSIUS "Celsius" -#define D_CO2 "CO2" +#define D_CO2 "Koolstofdioxide" #define D_CODE "code" // Button code #define D_COLDLIGHT "Koud" #define D_COMMAND "Opdracht" @@ -118,6 +118,7 @@ #define D_INITIALIZED "Geinitialiseerd" #define D_IP_ADDRESS "IP Adres" #define D_LIGHT "Ligt" +#define D_LIMIT "Grenswaarde" #define D_LOCAL_TIME "Plaatselijk" #define D_LOW "Laag" #define D_LWT "LWT" @@ -386,6 +387,7 @@ #define D_DOMOTICZ_COUNT "Count" #define D_DOMOTICZ_VOLTAGE "Spanning" #define D_DOMOTICZ_CURRENT "Stroom" + #define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_UPDATE_TIMER "Bijwerk timer" // xdrv_irremote.ino @@ -474,11 +476,13 @@ #define D_SENSOR_LED "Led" // Suffix "1i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Teller" // Suffix "1" +#define D_SENSOR_IRRECV "IRrecv" #define D_SENSOR_MHZ_RX "MHZ Rx" #define D_SENSOR_MHZ_TX "MHZ Tx" #define D_SENSOR_PZEM_RX "PZEM Rx" #define D_SENSOR_PZEM_TX "PZEM Tx" -#define D_SENSOR_IRRECV "IRrecv" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" #define D_SENSOR_BACKLIGHT "BLight" diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index 4fd42d4fc..51281d0fd 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -1,7 +1,7 @@ /* pl-PL.h - localization for Polish without fonetick - Poland for Sonoff-Tasmota - Copyright (C) 2017 Theo Arends (translated by roblad - Robert L.) + Copyright (C) 2018 Theo Arends (translated by roblad - Robert L.) 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 @@ -66,7 +66,7 @@ #define D_BUTTON "Przycisk" #define D_BY "by" // Written by me #define D_CELSIUS "Celsiusza" -#define D_CO2 "CO2" +#define D_CO2 "Dwutlenku węgla" #define D_CODE "kod" // Button code #define D_COLDLIGHT "Zimny" #define D_COMMAND "Komenda" @@ -118,6 +118,7 @@ #define D_INITIALIZED "Zainicjowany" #define D_IP_ADDRESS "Adres IP" #define D_LIGHT "Swiatlo" +#define D_LIMIT "Wartość graniczna" #define D_LOCAL_TIME "Lokalny" #define D_LOW "Niski" #define D_LWT "LWT" @@ -386,6 +387,7 @@ #define D_DOMOTICZ_COUNT "Licznik" #define D_DOMOTICZ_VOLTAGE "Napiecie" #define D_DOMOTICZ_CURRENT "Prad" + #define D_DOMOTICZ_AIRQUALITY "AirQuality" #define D_DOMOTICZ_UPDATE_TIMER "Zaktualizuj czasomierz" // xdrv_irremote.ino @@ -479,6 +481,8 @@ #define D_SENSOR_MHZ_TX "MHZ Tx" #define D_SENSOR_PZEM_RX "PZEM Rx" #define D_SENSOR_PZEM_TX "PZEM Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" #define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_DC "SPI DC" #define D_SENSOR_BACKLIGHT "BLight" diff --git a/sonoff/settings.h b/sonoff/settings.h index 049d6a000..b718bf005 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -1,7 +1,7 @@ /* settings.h - setting variables 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 @@ -201,8 +201,8 @@ struct SYSCFG { byte free_451[2]; // 451 uint8_t sleep; // 453 - uint16_t domoticz_switch_idx[MAX_DOMOTICZ_IDX]; // 454 - uint16_t domoticz_sensor_idx[12]; // 45C + uint16_t domoticz_switch_idx[MAX_DOMOTICZ_IDX]; // 454 + uint16_t domoticz_sensor_idx[MAX_DOMOTICZ_SNS_IDX]; // 45C uint8_t module; // 474 uint8_t ws_color[4][3]; // 475 diff --git a/sonoff/settings.ino b/sonoff/settings.ino index 1a2035c22..c6ed275db 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -1,7 +1,7 @@ /* settings.ino - user settings 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 @@ -182,7 +182,7 @@ void SettingsSaveAll() } else { Settings.power = 0; } - XsnsCall(FUNC_XSNS_SAVE_STATE); + XsnsCall(FUNC_SAVE_BEFORE_RESTART); SettingsSave(0); } diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index e2e1553f9..558e4d6ab 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -1,7 +1,7 @@ /* sonoff.h - Master header file 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 @@ -46,6 +46,7 @@ typedef unsigned long power_t; // Power (Relay) type #define MAX_PULSETIMERS 8 // Max number of supported pulse timers #define MAX_FRIENDLYNAMES 4 // Max number of Friendly names #define MAX_DOMOTICZ_IDX 4 // Max number of Domoticz device, key and switch indices +#define MAX_DOMOTICZ_SNS_IDX 12 // Max number of Domoticz sensors indices #define MODULE SONOFF_BASIC // [Module] Select default model @@ -132,7 +133,7 @@ enum LightTypes {LT_BASIC, LT_PWM1, LT_PWM2, LT_PWM3, LT_PWM4, LT_PWM5, LT_PWM6, enum LichtSubtypes {LST_NONE, LST_SINGLE, LST_COLDWARM, LST_RGB, LST_RGBW, LST_RGBWC}; enum LichtSchemes {LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX}; -enum XsnsFunctions {FUNC_XSNS_INIT, FUNC_XSNS_EVERY_SECOND, FUNC_XSNS_PREP, FUNC_XSNS_JSON_APPEND, FUNC_XSNS_WEB, FUNC_XSNS_SAVE_STATE}; +enum XsnsFunctions {FUNC_INIT, FUNC_EVERY_50_MSECOND, FUNC_EVERY_SECOND, FUNC_PREP_BEFORE_TELEPERIOD, FUNC_JSON_APPEND, FUNC_WEB_APPEND, FUNC_SAVE_BEFORE_RESTART}; const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 }; diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 2db39f36a..a08318e2a 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -1,7 +1,7 @@ /* sonoff.ino - Sonoff-Tasmota firmware for iTead Sonoff, Wemos and NodeMCU hardware - 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 @@ -25,8 +25,8 @@ - Select IDE Tools - Flash Size: "1M (no SPIFFS)" ====================================================*/ -#define VERSION 0x050A0002 -#define VERSION_STRING "5.10.0b" // Would be great to have a macro that fills this from VERSION ... +#define VERSION 0x050A0003 +#define VERSION_STRING "5.10.0c" // Would be great to have a macro that fills this from VERSION ... // Location specific includes #include "sonoff.h" // Enumaration used in user_config.h @@ -1100,15 +1100,15 @@ void MqttDataCallback(char* topic, byte* data, unsigned int data_len) else if (CMND_MODULES == command_code) { for (byte i = 0; i < MAXMODULE; i++) { if (!jsflg) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_MODULES "%d\":\""), lines); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_MODULES "%d\":["), lines); } else { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); } jsflg = 1; snprintf_P(stemp1, sizeof(stemp1), kModules[i].name); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%d (%s)"), mqtt_data, i +1, stemp1); - if ((strlen(mqtt_data) > 200) || (i == MAXMODULE -1)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"}"), mqtt_data); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"%d (%s)\""), mqtt_data, i +1, stemp1); + if ((strlen(mqtt_data) > 300) || (i == MAXMODULE -1)) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s]}"), mqtt_data); MqttPublishPrefixTopic_P(5, type); jsflg = 0; lines++; @@ -1149,15 +1149,15 @@ void MqttDataCallback(char* topic, byte* data, unsigned int data_len) else if (CMND_GPIOS == command_code) { for (byte i = 0; i < GPIO_SENSOR_END; i++) { if (!jsflg) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_GPIOS "%d\":\""), lines); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_GPIOS "%d\":["), lines); } else { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); } jsflg = 1; snprintf_P(stemp1, sizeof(stemp1), kSensors[i]); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%d (%s)"), mqtt_data, i, stemp1); - if ((strlen(mqtt_data) > 200) || (i == GPIO_SENSOR_END -1)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"}"), mqtt_data); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"%d (%s)\""), mqtt_data, i, stemp1); + if ((strlen(mqtt_data) > 300) || (i == GPIO_SENSOR_END -1)) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s]}"), mqtt_data); MqttPublishPrefixTopic_P(5, type); jsflg = 0; lines++; @@ -1812,7 +1812,7 @@ boolean MqttShowSensor() snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_SWITCH "%d\":\"%s\""), mqtt_data, i +1, GetStateText(swm ^ lastwallswitch[i])); } } - XsnsCall(FUNC_XSNS_JSON_APPEND); + XsnsCall(FUNC_JSON_APPEND); boolean json_data_available = (strlen(mqtt_data) - json_data_start); if (strstr_P(mqtt_data, PSTR(D_TEMPERATURE))) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_TEMPERATURE_UNIT "\":\"%c\""), mqtt_data, TempUnit()); @@ -1871,7 +1871,7 @@ void PerformEverySecond() if (Settings.tele_period) { tele_period++; if (tele_period == Settings.tele_period -1) { - XsnsCall(FUNC_XSNS_PREP); + XsnsCall(FUNC_PREP_BEFORE_TELEPERIOD); } if (tele_period >= Settings.tele_period) { tele_period = 0; @@ -1887,7 +1887,7 @@ void PerformEverySecond() } } - XsnsCall(FUNC_XSNS_EVERY_SECOND); + XsnsCall(FUNC_EVERY_SECOND); if ((2 == RtcTime.minute) && latest_uptime_flag) { latest_uptime_flag = false; @@ -2220,6 +2220,8 @@ void StateLoop() LightAnimate(); } + XsnsCall(FUNC_EVERY_50_MSECOND); + /*-------------------------------------------------------------------------------------------*\ * Every 0.2 second \*-------------------------------------------------------------------------------------------*/ @@ -2521,17 +2523,20 @@ void GpioInit() #endif // USE_I2C devices_present = 1; + + light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0 if (Settings.flag.pwm_control) { - light_type = LT_BASIC; for (byte i = 0; i < MAX_PWMS; i++) { if (pin[GPIO_PWM1 +i] < 99) { light_type++; // Use Dimmer/Color control for all PWM as SetOption15 = 1 } } } + if (SONOFF_BRIDGE == Settings.module) { baudrate = 19200; } + if (SONOFF_DUAL == Settings.module) { devices_present = 2; baudrate = 19200; @@ -2544,11 +2549,6 @@ void GpioInit() devices_present = 0; baudrate = 19200; } - else if ((H801 == Settings.module) || (MAGICHOME == Settings.module) || (ARILUX_LC01 == Settings.module) || (ARILUX_LC11 == Settings.module)) { // PWM RGBCW led - if (!Settings.flag.pwm_control) { - light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0 - } - } else if (SONOFF_BN == Settings.module) { // PWM Single color led (White) light_type = LT_PWM1; } @@ -2572,6 +2572,7 @@ void GpioInit() } } } + for (byte i = 0; i < MAX_KEYS; i++) { if (pin[GPIO_KEY1 +i] < 99) { pinMode(pin[GPIO_KEY1 +i], (16 == pin[GPIO_KEY1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP); @@ -2599,7 +2600,7 @@ void GpioInit() if (light_type) { // Any Led light under Dimmer/Color control LightInit(); } else { - for (byte i = 0; i < MAX_PWMS; i++) { + for (byte i = 0; i < MAX_PWMS; i++) { // Basic PWM control only if (pin[GPIO_PWM1 +i] < 99) { pinMode(pin[GPIO_PWM1 +i], OUTPUT); analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); @@ -2623,8 +2624,6 @@ void GpioInit() } #endif // USE_IR_RECEIVE #endif // USE_IR_REMOTE - -// energy_flg = (((pin[GPIO_HLW_SEL] < 99) && (pin[GPIO_HLW_CF1] < 99) && (pin[GPIO_HLW_CF] < 99)) || ((pin[GPIO_PZEM_RX] < 99) && (pin[GPIO_PZEM_TX]))); } extern "C" { diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index 7a16e17d0..56e696573 100644 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -1,7 +1,7 @@ /* sonoff_post.h - Post header file 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 diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index cbe0037a7..268288d88 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -1,7 +1,7 @@ /* sonoff_template.h - template settings 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 @@ -82,6 +82,8 @@ enum UserSelectablePins { GPIO_MHZ_RXD, // MH-Z19 Serial interface GPIO_PZEM_TX, // PZEM004T Serial interface GPIO_PZEM_RX, // PZEM004T Serial interface + GPIO_SAIR_TX, // SenseAir Serial interface + GPIO_SAIR_RX, // SenseAir Serial interface GPIO_SENSOR_END }; // Text in webpage Module Parameters and commands GPIOS and GPIO @@ -145,7 +147,9 @@ const char kSensors[GPIO_SENSOR_END][9] PROGMEM = { D_SENSOR_MHZ_TX, D_SENSOR_MHZ_RX, D_SENSOR_PZEM_TX, - D_SENSOR_PZEM_RX + D_SENSOR_PZEM_RX, + D_SENSOR_SAIR_TX, + D_SENSOR_SAIR_RX }; // Programmer selectable GPIO functionality offset by user selectable GPIOs @@ -205,6 +209,7 @@ enum SupportedModules { ARILUX_LC01, ARILUX_LC11, SONOFF_DUAL_R2, + ARILUX_LC06, MAXMODULE }; /********************************************************************************************/ @@ -256,6 +261,7 @@ const uint8_t kNiceList[MAXMODULE] PROGMEM = { H801, MAGICHOME, ARILUX_LC01, + ARILUX_LC06, ARILUX_LC11, HUAFAN_SS, KMC_70011, @@ -743,7 +749,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO14 Relay 0, 0, 0 }, - { "Arilux", // Arilux AL-LC01 (ESP8285) - https://www.banggood.com/nl/ARILUX-AL-LC01-Super-Mini-LED-WIFI-Smart-RGB-Controller-For-RGB-LED-Strip-Light-DC-9-12V-p-1058603.html + { "Arilux LC01", // Arilux AL-LC01 (ESP8285) - https://www.banggood.com/nl/ARILUX-AL-LC01-Super-Mini-LED-WIFI-Smart-RGB-Controller-For-RGB-LED-Strip-Light-DC-9-12V-p-1058603.html // (PwmFrequency 1111Hz) GPIO_KEY1, // GPIO00 Optional Button 0, @@ -786,7 +792,38 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO12 Relay 1 (0 = Off, 1 = On) GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) 0, 0, 0, 0 + }, + { "Arilux LC06", // Arilux AL-LC06 (ESP8285) - https://www.banggood.com/ARILUX-AL-LC06-LED-WIFI-Smartphone-Controller-Romote-5-Channels-DC12-24V-For-RGBWW-Strip-light-p-1061476.html + GPIO_KEY1, // GPIO00 Optional Button + 0, + GPIO_USER, // GPIO02 Empty pad + 0, + GPIO_USER, // GPIO04 W2 - PWM5 + 0, + 0, 0, 0, 0, 0, 0, // Flash connection + GPIO_PWM2, // GPIO12 RGB LED Green + GPIO_PWM3, // GPIO13 RGB LED Blue + GPIO_PWM1, // GPIO14 RGB LED Red + GPIO_USER, // GPIO15 RGBW LED White + 0, 0 } }; +/* + Optionals + + { "Xenon 3CH", // Xenon 3CH (ESP8266) - (#1128) + 0, 0, 0, + GPIO_KEY2, // GPIO03 Serial TXD and Optional sensor + GPIO_REL2, // GPIO04 Relay 2 + GPIO_KEY3, // GPIO05 Input 2 + 0, 0, 0, 0, 0, 0, // Flash connection + GPIO_KEY1, // GPIO12 Key input + GPIO_REL1, // GPIO13 Relay 1 + 0, + GPIO_REL3, // GPIO15 Relay 3 + 0, 0 + } +*/ + #endif // _SONOFF_TEMPLATE_H_ \ No newline at end of file diff --git a/sonoff/support.ino b/sonoff/support.ino index dd6d6c92d..bee00b2ca 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -1,7 +1,7 @@ /* support.ino - 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 @@ -1337,15 +1337,15 @@ boolean Xsns02(byte function) if (pin[GPIO_ADC0] < 99) { switch (function) { -// case FUNC_XSNS_INIT: +// case FUNC_INIT: // break; -// case FUNC_XSNS_PREP: +// case FUNC_PREP_BEFORE_TELEPERIOD: // break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: AdcShow(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: AdcShow(0); break; #endif // USE_WEBSERVER diff --git a/sonoff/user_config.h b/sonoff/user_config.h index bb73b3943..dbeb0cca9 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -1,7 +1,7 @@ /* user_config.h - user specific configuration 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 @@ -163,7 +163,7 @@ // -- Sensor code selection ----------------------- #define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices -//#define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k3 code) +//#define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) // WARNING: Select none for default one DS18B20 sensor or enable one of the following two options for multiple sensors //#define USE_DS18x20 // Optional for more than one DS18x20 sensors with id sort, single scan and read retry (+1k3 code) @@ -192,9 +192,8 @@ #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 (+2k code) +//#define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code) #define USE_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code) diff --git a/sonoff/user_config_override.h b/sonoff/user_config_override.h index 37fe10e6c..1008faf72 100644 --- a/sonoff/user_config_override.h +++ b/sonoff/user_config_override.h @@ -1,7 +1,7 @@ /* user_config_override.h - user configuration overrides user_config.h 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 diff --git a/sonoff/webserver.ino b/sonoff/webserver.ino index 0f08635d0..21242702f 100644 --- a/sonoff/webserver.ino +++ b/sonoff/webserver.ino @@ -1,7 +1,7 @@ /* webserver.ino - webserver 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 @@ -511,7 +511,7 @@ void HandleAjaxStatusRefresh() String page = ""; mqtt_data[0] = '\0'; - XsnsCall(FUNC_XSNS_WEB); + XsnsCall(FUNC_WEB_APPEND); if (strlen(mqtt_data)) { page += FPSTR(HTTP_TABLE100); page += mqtt_data; diff --git a/sonoff/xdrv_domoticz.ino b/sonoff/xdrv_domoticz.ino index 1a0a1c0ac..f1e478d1f 100644 --- a/sonoff/xdrv_domoticz.ino +++ b/sonoff/xdrv_domoticz.ino @@ -1,7 +1,7 @@ /* xdrv_domoticz.ino - domoticz 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 @@ -40,10 +40,14 @@ enum DomoticzCommands { const char kDomoticzCommands[] PROGMEM = D_CMND_IDX "|" D_CMND_KEYIDX "|" D_CMND_SWITCHIDX "|" D_CMND_SENSORIDX "|" D_CMND_UPDATETIMER ; -enum DomoticzSensors {DZ_TEMP, DZ_TEMP_HUM, DZ_TEMP_HUM_BARO, DZ_POWER_ENERGY, DZ_ILLUMINANCE, DZ_COUNT, DZ_VOLTAGE, DZ_CURRENT, DZ_MAX_SENSORS}; +enum DomoticzSensors {DZ_TEMP, DZ_TEMP_HUM, DZ_TEMP_HUM_BARO, DZ_POWER_ENERGY, DZ_ILLUMINANCE, DZ_COUNT, DZ_VOLTAGE, DZ_CURRENT, DZ_AIRQUALITY, DZ_MAX_SENSORS}; + +#if MAX_DOMOTICZ_SNS_IDX < DZ_MAX_SENSORS + #error "Domoticz: Too many sensors or change settings.h layout" +#endif const char kDomoticzSensors[] PROGMEM = - D_DOMOTICZ_TEMP "|" D_DOMOTICZ_TEMP_HUM "|" D_DOMOTICZ_TEMP_HUM_BARO "|" D_DOMOTICZ_POWER_ENERGY "|" D_DOMOTICZ_ILLUMINANCE "|" D_DOMOTICZ_COUNT "|" D_DOMOTICZ_VOLTAGE "|" D_DOMOTICZ_CURRENT ; + D_DOMOTICZ_TEMP "|" D_DOMOTICZ_TEMP_HUM "|" D_DOMOTICZ_TEMP_HUM_BARO "|" D_DOMOTICZ_POWER_ENERGY "|" D_DOMOTICZ_ILLUMINANCE "|" D_DOMOTICZ_COUNT "|" D_DOMOTICZ_VOLTAGE "|" D_DOMOTICZ_CURRENT "|" D_DOMOTICZ_AIRQUALITY ; const char S_JSON_DOMOTICZ_COMMAND_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_DOMOTICZ "%s%d\":%d}"; @@ -254,6 +258,19 @@ boolean DomoticzButton(byte key, byte device, byte state, byte svalflg) /*********************************************************************************************\ * Sensors + * + * Source : https://www.domoticz.com/wiki/Domoticz_API/JSON_URL%27s + * https://www.domoticz.com/wiki/MQTT + * + * Percentage, Barometric, Air Quality: + * {\"idx\":%d,\"nvalue\":%s}, Idx, Value + * + * Humidity: + * {\"idx\":%d,\"nvalue\":%s,\"svalue\":\"%s\"}, Idx, Humidity, HumidityStatus + * + * All other: + * {\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}, Idx, Value(s) + * \*********************************************************************************************/ uint8_t DomoticzHumidityState(char *hum) @@ -268,8 +285,11 @@ void DomoticzSensor(byte idx, char *data) char dmess[64]; memcpy(dmess, mqtt_data, sizeof(dmess)); - snprintf_P(mqtt_data, sizeof(dmess), PSTR("{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}"), - Settings.domoticz_sensor_idx[idx], data); + if (DZ_AIRQUALITY == idx) { + snprintf_P(mqtt_data, sizeof(dmess), PSTR("{\"idx\":%d,\"nvalue\":%s}"), Settings.domoticz_sensor_idx[idx], data); + } else { + snprintf_P(mqtt_data, sizeof(dmess), PSTR("{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}"), Settings.domoticz_sensor_idx[idx], data); + } MqttPublish(domoticz_in_topic); memcpy(mqtt_data, dmess, sizeof(dmess)); } @@ -353,6 +373,7 @@ void HandleDomoticzConfiguration() void DomoticzSaveSettings() { char stemp[20]; + char ssensor_indices[6 * MAX_DOMOTICZ_SNS_IDX]; for (byte i = 0; i < MAX_DOMOTICZ_IDX; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("r%d"), i +1); @@ -362,20 +383,19 @@ void DomoticzSaveSettings() snprintf_P(stemp, sizeof(stemp), PSTR("s%d"), i +1); Settings.domoticz_switch_idx[i] = (!strlen(WebServer->arg(stemp).c_str())) ? 0 : atoi(WebServer->arg(stemp).c_str()); } + ssensor_indices[0] = '\0'; for (byte i = 0; i < DZ_MAX_SENSORS; i++) { snprintf_P(stemp, sizeof(stemp), PSTR("l%d"), i +1); Settings.domoticz_sensor_idx[i] = (!strlen(WebServer->arg(stemp).c_str())) ? 0 : atoi(WebServer->arg(stemp).c_str()); + snprintf_P(ssensor_indices, sizeof(ssensor_indices), PSTR("%s%s%d"), ssensor_indices, (strlen(ssensor_indices)) ? "," : "", Settings.domoticz_sensor_idx[i]); } Settings.domoticz_update_timer = (!strlen(WebServer->arg("ut").c_str())) ? DOMOTICZ_UPDATE_TIMER : atoi(WebServer->arg("ut").c_str()); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DOMOTICZ D_CMND_IDX " %d, %d, %d, %d, " D_CMND_UPDATETIMER " %d"), + + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DOMOTICZ D_CMND_IDX " %d,%d,%d,%d, " D_CMND_KEYIDX " %d,%d,%d,%d, " D_CMND_SWITCHIDX " %d,%d,%d,%d, " D_CMND_SENSORIDX " %s, " D_CMND_UPDATETIMER " %d"), Settings.domoticz_relay_idx[0], Settings.domoticz_relay_idx[1], Settings.domoticz_relay_idx[2], Settings.domoticz_relay_idx[3], - Settings.domoticz_update_timer); - AddLog(LOG_LEVEL_INFO); - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DOMOTICZ D_CMND_KEYIDX " %d, %d, %d, %d, " D_CMND_SWITCHIDX " %d, %d, %d, %d, " D_CMND_SENSORIDX " %d, %d, %d, %d, %d, %d, %d, %d"), Settings.domoticz_key_idx[0], Settings.domoticz_key_idx[1], Settings.domoticz_key_idx[2], Settings.domoticz_key_idx[3], Settings.domoticz_switch_idx[0], Settings.domoticz_switch_idx[1], Settings.domoticz_switch_idx[2], Settings.domoticz_switch_idx[3], - Settings.domoticz_sensor_idx[0], Settings.domoticz_sensor_idx[1], Settings.domoticz_sensor_idx[2], Settings.domoticz_sensor_idx[3], - Settings.domoticz_sensor_idx[4], Settings.domoticz_sensor_idx[5], Settings.domoticz_sensor_idx[6], Settings.domoticz_sensor_idx[7]); + ssensor_indices, Settings.domoticz_update_timer); AddLog(LOG_LEVEL_INFO); } #endif // USE_WEBSERVER diff --git a/sonoff/xdrv_irremote.ino b/sonoff/xdrv_irremote.ino index 423270684..8a54adf8b 100644 --- a/sonoff/xdrv_irremote.ino +++ b/sonoff/xdrv_irremote.ino @@ -1,7 +1,7 @@ /* xdrv_irremote.ino - infra red support for Sonoff-Tasmota - Copyright (C) 2017 Heiko Krupp, Lazar Obradovic and Theo Arends + Copyright (C) 2018 Heiko Krupp, Lazar Obradovic 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 diff --git a/sonoff/xdrv_light.ino b/sonoff/xdrv_light.ino index 2eff11895..3b82f74cf 100644 --- a/sonoff/xdrv_light.ino +++ b/sonoff/xdrv_light.ino @@ -1,7 +1,7 @@ /* xdrv_light.ino - PWM, WS2812 and sonoff led 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 diff --git a/sonoff/xdrv_snfbridge.ino b/sonoff/xdrv_snfbridge.ino index d475860dd..6c7993cd4 100644 --- a/sonoff/xdrv_snfbridge.ino +++ b/sonoff/xdrv_snfbridge.ino @@ -1,7 +1,7 @@ /* xdrv_snfbridge.ino - sonoff RF bridge 433 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 diff --git a/sonoff/xdrv_wemohue.ino b/sonoff/xdrv_wemohue.ino index 05d4ed7ef..082360235 100755 --- a/sonoff/xdrv_wemohue.ino +++ b/sonoff/xdrv_wemohue.ino @@ -1,7 +1,7 @@ /* xdrv_wemohue.ino - wemo and hue support for Sonoff-Tasmota - Copyright (C) 2017 Heiko Krupp and Theo Arends + Copyright (C) 2018 Heiko Krupp 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 @@ -268,6 +268,17 @@ const char WEMO_EVENTSERVICE_XML[] PROGMEM = "" "\r\n" "\r\n"; + +const char WEMO_RESPONSE_STATE_SOAP[] PROGMEM = + "" + "" + "" + "{x1" + "" + "" + "\r\n" + "\r\n"; + const char WEMO_SETUP_XML[] PROGMEM = "" "" @@ -299,17 +310,21 @@ void HandleUpnpEvent() { AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_BASIC_EVENT)); String request = WebServer->arg(0); + String state_xml = FPSTR(WEMO_RESPONSE_STATE_SOAP); + //differentiate get and set state if (request.indexOf(F("SetBinaryState")) > 0) { if (request.indexOf(F("State>1 0) { - // ExecuteCommandPower(1, 1); ExecuteCommandPower(devices_present, 1); } - if (request.indexOf(F("State>0 0) { - // ExecuteCommandPower(1, 0); + else if (request.indexOf(F("State>0 0) { ExecuteCommandPower(devices_present, 0); } } - WebServer->send(200, FPSTR(HDR_CTYPE_PLAIN), ""); + else if(request.indexOf(F("GetBinaryState")) > 0){ + state_xml.replace("Set", "Get"); + } + state_xml.replace("{x1", String(bitRead(power, devices_present -1))); + WebServer->send(200, FPSTR(HDR_CTYPE_XML), state_xml); } void HandleUpnpService() diff --git a/sonoff/xdrv_ws2812.ino b/sonoff/xdrv_ws2812.ino index 8add8c513..2aea22054 100644 --- a/sonoff/xdrv_ws2812.ino +++ b/sonoff/xdrv_ws2812.ino @@ -1,7 +1,7 @@ /* xdrv_ws2812.ino - ws2812 led string support for Sonoff-Tasmota - Copyright (C) 2017 Heiko Krupp and Theo Arends + Copyright (C) 2018 Heiko Krupp 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 diff --git a/sonoff/xsns_01_counter.ino b/sonoff/xsns_01_counter.ino index 007662aff..316445dfb 100644 --- a/sonoff/xsns_01_counter.ino +++ b/sonoff/xsns_01_counter.ino @@ -1,7 +1,7 @@ /* xsns_01_counter.ino - Counter sensors (water meters, electricity meters etc.) sensor support for Sonoff-Tasmota - Copyright (C) 2017 Maarten Damen and Theo Arends + Copyright (C) 2018 Maarten Damen 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 @@ -130,20 +130,18 @@ boolean Xsns01(byte function) boolean result = false; switch (function) { - case FUNC_XSNS_INIT: + case FUNC_INIT: CounterInit(); break; -// case FUNC_XSNS_PREP: -// break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: CounterShow(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: CounterShow(0); break; #endif // USE_WEBSERVER - case FUNC_XSNS_SAVE_STATE: + case FUNC_SAVE_BEFORE_RESTART: CounterSaveState(); break; } diff --git a/sonoff/xsns_03_energy.ino b/sonoff/xsns_03_energy.ino index 649ea2993..4d67fb2c4 100644 --- a/sonoff/xsns_03_energy.ino +++ b/sonoff/xsns_03_energy.ino @@ -1,7 +1,7 @@ /* xsns_03_energy.ino - HLW8012 (Sonoff Pow) and PZEM004T energy 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 @@ -41,7 +41,6 @@ const char kEnergyCommands[] PROGMEM = D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|" D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW ; -bool energy_power_factor_ready = false; float energy_voltage = 0; // 123.1 V float energy_current = 0; // 123.123 A float energy_power = 0; // 123.1 W @@ -208,25 +207,6 @@ void HlwEvery200ms() hlw_cf1_summed_pulse_length = 0; hlw_cf1_pulse_counter = 0; } - -/* - energy_power = 0; - if (hlw_cf_pulse_length && (power &1) && !hlw_load_off) { - hlw_w = (HLW_PREF * Settings.hlw_power_calibration) / hlw_cf_pulse_length; - energy_power = (float)hlw_w / 10; - } - energy_voltage = 0; - if (hlw_cf1_voltage_pulse_length && (power &1)) { // If powered on always provide voltage - hlw_u = (HLW_UREF * Settings.hlw_voltage_calibration) / hlw_cf1_voltage_pulse_length; - energy_voltage = (float)hlw_u / 10; - } - energy_current = 0; - if (hlw_cf1_current_pulse_length && energy_power) { // No current if no power being consumed - hlw_i = (HLW_IREF * Settings.hlw_current_calibration) / hlw_cf1_current_pulse_length; - energy_current = (float)hlw_i / 1000; - } -*/ - energy_power_factor_ready = true; } void HlwInit() @@ -269,119 +249,9 @@ void HlwInit() * Based on: PZEM004T library https://github.com/olehs/PZEM004T \*********************************************************************************************/ -#define PZEM_BAUD_RATE 9600 +#include -/*********************************************************************************************\ - * Subset SoftwareSerial -\*********************************************************************************************/ - -#define PZEM_SERIAL_BUFFER_SIZE 20 -#define PZEM_SERIAL_WAIT { while (ESP.getCycleCount() -start < wait) optimistic_yield(1); wait += pzem_serial_bit_time; } - -uint8_t pzem_serial_rx_pin; -uint8_t pzem_serial_tx_pin; -uint8_t pzem_serial_in_pos = 0; -uint8_t pzem_serial_out_pos = 0; -uint8_t pzem_serial_buffer[PZEM_SERIAL_BUFFER_SIZE]; -unsigned long pzem_serial_bit_time; -unsigned long pzem_serial_bit_time_start; - -bool PzemSerialValidGpioPin(uint8_t pin) { - return (pin >= 0 && pin <= 5) || (pin >= 9 && pin <= 10) || (pin >= 12 && pin <= 15); -} - -bool PzemSerial(uint8_t receive_pin, uint8_t transmit_pin) -{ - if (!((PzemSerialValidGpioPin(receive_pin)) && (PzemSerialValidGpioPin(transmit_pin) || transmit_pin == 16))) { - return false; - } - pzem_serial_rx_pin = receive_pin; - pinMode(pzem_serial_rx_pin, INPUT); - attachInterrupt(pzem_serial_rx_pin, PzemSerialRxRead, FALLING); - - pzem_serial_tx_pin = transmit_pin; - pinMode(pzem_serial_tx_pin, OUTPUT); - digitalWrite(pzem_serial_tx_pin, 1); - - pzem_serial_bit_time = ESP.getCpuFreqMHz() *1000000 /PZEM_BAUD_RATE; // 8333 - pzem_serial_bit_time_start = pzem_serial_bit_time + pzem_serial_bit_time /3 -500; // 10610 ICACHE_RAM_ATTR start delay -// pzem_serial_bit_time_start = pzem_serial_bit_time; // Non ICACHE_RAM_ATTR start delay (experimental) - - return true; -} - -int PzemSerialRead() { - if (pzem_serial_in_pos == pzem_serial_out_pos) { - return -1; - } - int ch = pzem_serial_buffer[pzem_serial_out_pos]; - pzem_serial_out_pos = (pzem_serial_out_pos +1) % PZEM_SERIAL_BUFFER_SIZE; - return ch; -} - -int PzemSerialAvailable() { - int avail = pzem_serial_in_pos - pzem_serial_out_pos; - if (avail < 0) { - avail += PZEM_SERIAL_BUFFER_SIZE; - } - return avail; -} - -size_t PzemSerialTxWrite(uint8_t b) -{ - unsigned long wait = pzem_serial_bit_time; - digitalWrite(pzem_serial_tx_pin, HIGH); - unsigned long start = ESP.getCycleCount(); - // Start bit; - digitalWrite(pzem_serial_tx_pin, LOW); - PZEM_SERIAL_WAIT; - for (int i = 0; i < 8; i++) { - digitalWrite(pzem_serial_tx_pin, (b & 1) ? HIGH : LOW); - PZEM_SERIAL_WAIT; - b >>= 1; - } - // Stop bit - digitalWrite(pzem_serial_tx_pin, HIGH); - PZEM_SERIAL_WAIT; - return 1; -} - -size_t PzemSerialWrite(const uint8_t *buffer, size_t size = 1) { - size_t n = 0; - while(size--) { - n += PzemSerialTxWrite(*buffer++); - } - return n; -} - -//void PzemSerialRxRead() ICACHE_RAM_ATTR; // Add 215 bytes to iram usage -void PzemSerialRxRead() { - // Advance the starting point for the samples but compensate for the - // initial delay which occurs before the interrupt is delivered - unsigned long wait = pzem_serial_bit_time_start; - unsigned long start = ESP.getCycleCount(); - uint8_t rec = 0; - for (int i = 0; i < 8; i++) { - PZEM_SERIAL_WAIT; - rec >>= 1; - if (digitalRead(pzem_serial_rx_pin)) { - rec |= 0x80; - } - } - // Stop bit - PZEM_SERIAL_WAIT; - // Store the received value in the buffer unless we have an overflow - int next = (pzem_serial_in_pos +1) % PZEM_SERIAL_BUFFER_SIZE; - if (next != pzem_serial_out_pos) { - pzem_serial_buffer[pzem_serial_in_pos] = rec; - pzem_serial_in_pos = next; - } - // Must clear this bit in the interrupt register, - // it gets set even when interrupts are disabled - GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << pzem_serial_rx_pin); -} - -/*********************************************************************************************/ +TasmotaSerial *PzemSerial; #define PZEM_VOLTAGE (uint8_t)0xB0 #define RESP_VOLTAGE (uint8_t)0xA0 @@ -401,11 +271,7 @@ void PzemSerialRxRead() { #define PZEM_POWER_ALARM (uint8_t)0xB5 #define RESP_POWER_ALARM (uint8_t)0xA5 -#define RESPONSE_SIZE sizeof(PZEMCommand) -#define RESPONSE_DATA_SIZE RESPONSE_SIZE - 2 - #define PZEM_DEFAULT_READ_TIMEOUT 500 -#define PZEM_ERROR_VALUE -1.0 struct PZEMCommand { uint8_t command; @@ -416,52 +282,16 @@ struct PZEMCommand { IPAddress pzem_ip(192, 168, 1, 1); -float PZEM004T_voltage_rcv() +uint8_t PzemCrc(uint8_t *data) { - uint8_t data[RESPONSE_DATA_SIZE]; - - if (!PZEM004T_recieve(RESP_VOLTAGE, data)) { - return PZEM_ERROR_VALUE; + uint16_t crc = 0; + for (uint8_t i = 0; i < sizeof(PZEMCommand) -1; i++) { + crc += *data++; } - return (data[0] << 8) + data[1] + (data[2] / 10.0); // 65535.x V + return (uint8_t)(crc & 0xFF); } -float PZEM004T_current_rcv() -{ - uint8_t data[RESPONSE_DATA_SIZE]; - - if (!PZEM004T_recieve(RESP_CURRENT, data)) { - return PZEM_ERROR_VALUE; - } - return (data[0] << 8) + data[1] + (data[2] / 100.0); // 65535.xx A -} - -float PZEM004T_power_rcv() -{ - uint8_t data[RESPONSE_DATA_SIZE]; - - if (!PZEM004T_recieve(RESP_POWER, data)) { - return PZEM_ERROR_VALUE; - } - return (data[0] << 8) + data[1]; // 65535 W -} - -float PZEM004T_energy_rcv() -{ - uint8_t data[RESPONSE_DATA_SIZE]; - - if (!PZEM004T_recieve(RESP_ENERGY, data)) { - return PZEM_ERROR_VALUE; - } - return ((uint32_t)data[0] << 16) + ((uint16_t)data[1] << 8) + data[2]; // 16777215 Wh -} - -bool PZEM004T_setAddress_rcv() -{ - return PZEM004T_recieve(RESP_SET_ADDRESS, 0); -} - -void PZEM004T_send(uint8_t cmd) +void PzemSend(uint8_t cmd) { PZEMCommand pzem; @@ -472,117 +302,89 @@ void PZEM004T_send(uint8_t cmd) pzem.data = 0; uint8_t *bytes = (uint8_t*)&pzem; - pzem.crc = PZEM004T_crc(bytes, sizeof(pzem) - 1); + pzem.crc = PzemCrc(bytes); - while (PzemSerialAvailable()) { - PzemSerialRead(); - } - PzemSerialWrite(bytes, sizeof(pzem)); + PzemSerial->write(bytes, sizeof(pzem)); } -bool PZEM004T_isReady() +bool PzemReceiveReady() { - return PzemSerialAvailable() >= RESPONSE_SIZE; + return PzemSerial->available() >= sizeof(PZEMCommand); } -bool PZEM004T_recieve(uint8_t resp, uint8_t *data) +bool PzemRecieve(uint8_t resp, float *data) { - uint8_t buffer[RESPONSE_SIZE]; + uint8_t buffer[sizeof(PZEMCommand)]; - unsigned long startTime = millis(); + unsigned long start = millis(); uint8_t len = 0; -// while ((len < RESPONSE_SIZE) && (millis() - startTime < PZEM_DEFAULT_READ_TIMEOUT)) { - while ((len < RESPONSE_SIZE) && (millis() - startTime < PZEM_DEFAULT_READ_TIMEOUT)) { - if (PzemSerialAvailable() > 0) { - uint8_t c = (uint8_t)PzemSerialRead(); + while ((len < sizeof(PZEMCommand)) && (millis() - start < PZEM_DEFAULT_READ_TIMEOUT)) { + if (PzemSerial->available() > 0) { + uint8_t c = (uint8_t)PzemSerial->read(); if (!c && !len) { continue; // skip 0 at startup } buffer[len++] = c; } -// yield(); // do background netw tasks while blocked for IO (prevents ESP watchdog trigger) - This triggers Watchdog!!! } - if (len != RESPONSE_SIZE) { + if (len != sizeof(PZEMCommand)) { +// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Pzem comms timeout")); return false; } - if (buffer[6] != PZEM004T_crc(buffer, len - 1)) { + if (buffer[6] != PzemCrc(buffer)) { +// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Pzem crc error")); return false; } if (buffer[0] != resp) { +// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Pzem bad response")); return false; } - if (data) { - for (int i = 0; i < RESPONSE_DATA_SIZE; i++) { - data[i] = buffer[1 + i]; - } - } + switch (resp) { + case RESP_VOLTAGE: + *data = (float)(buffer[1] << 8) + buffer[2] + (buffer[3] / 10.0); // 65535.x V + break; + case RESP_CURRENT: + *data = (float)(buffer[1] << 8) + buffer[2] + (buffer[3] / 100.0); // 65535.xx A + break; + case RESP_POWER: + *data = (float)(buffer[1] << 8) + buffer[2]; // 65535 W + break; + case RESP_ENERGY: + *data = (float)((uint32_t)buffer[1] << 16) + ((uint16_t)buffer[2] << 8) + buffer[3]; // 16777215 Wh + break; + } return true; } -uint8_t PZEM004T_crc(uint8_t *data, uint8_t sz) -{ - uint16_t crc = 0; - for (uint8_t i = 0; i < sz; i++) { - crc += *data++; - } - return (uint8_t)(crc & 0xFF); -} - /*********************************************************************************************/ -typedef enum -{ - SET_ADDRESS, - READ_VOLTAGE, - READ_CURRENT, - READ_POWER, - READ_ENERGY, -} PZEMReadStates; +const uint8_t pzem_commands[] { PZEM_SET_ADDRESS, PZEM_VOLTAGE, PZEM_CURRENT, PZEM_POWER, PZEM_ENERGY }; +const uint8_t pzem_responses[] { RESP_SET_ADDRESS, RESP_VOLTAGE, RESP_CURRENT, RESP_POWER, RESP_ENERGY }; -PZEMReadStates pzem_read_state = SET_ADDRESS; - -byte pzem_sendRetry = 0; +uint8_t pzem_read_state = 0; +uint8_t pzem_sendRetry = 0; void PzemEvery200ms() { - bool dataReady = PZEM004T_isReady(); + bool data_ready = PzemReceiveReady(); - if (dataReady) { - float pzem_value; - switch (pzem_read_state) { - case SET_ADDRESS: - if (PZEM004T_setAddress_rcv()) { - pzem_read_state = READ_VOLTAGE; - } - break; - case READ_VOLTAGE: - pzem_value = PZEM004T_voltage_rcv(); - if (pzem_value != PZEM_ERROR_VALUE) { - energy_voltage = pzem_value; // 230.2V - pzem_read_state = READ_CURRENT; - } - break; - case READ_CURRENT: - pzem_value = PZEM004T_current_rcv(); - if (pzem_value != PZEM_ERROR_VALUE) { - energy_current = pzem_value; // 17.32A - pzem_read_state = READ_POWER; - } - break; - case READ_POWER: - pzem_value = PZEM004T_power_rcv(); - if (pzem_value != PZEM_ERROR_VALUE) { - energy_power = pzem_value; // 20W - energy_power_factor_ready = true; - pzem_read_state = READ_ENERGY; - } - break; - case READ_ENERGY: - pzem_value = PZEM004T_energy_rcv(); - if (pzem_value != PZEM_ERROR_VALUE) { - energy_total = pzem_value / 1000; // 99999Wh + if (data_ready) { + float value = 0; + if (PzemRecieve(pzem_responses[pzem_read_state], &value)) { + switch (pzem_read_state) { + case 1: + energy_voltage = value; // 230.2V + break; + case 2: + energy_current = value; // 17.32A + break; + case 3: + energy_power = value; // 20W + break; + case 4: + energy_total = value / 1000; // 99999Wh if (!energy_startup) { if (energy_total < energy_start) { energy_start = energy_total; @@ -591,32 +393,18 @@ void PzemEvery200ms() energy_kWhtoday = (energy_total - energy_start) * 100000000; energy_daily = (float)energy_kWhtoday / 100000000; } - pzem_read_state = READ_VOLTAGE; - } - break; + break; + } + pzem_read_state++; + if (5 == pzem_read_state) { + pzem_read_state = 1; + } } } - if (0 == pzem_sendRetry || dataReady) { + if (0 == pzem_sendRetry || data_ready) { pzem_sendRetry = 5; - - switch (pzem_read_state) { - case SET_ADDRESS: - PZEM004T_send(PZEM_SET_ADDRESS); - break; - case READ_VOLTAGE: - PZEM004T_send(PZEM_VOLTAGE); - break; - case READ_CURRENT: - PZEM004T_send(PZEM_CURRENT); - break; - case READ_POWER: - PZEM004T_send(PZEM_POWER); - break; - case READ_ENERGY: - PZEM004T_send(PZEM_ENERGY); - break; - } + PzemSend(pzem_commands[pzem_read_state]); } else { pzem_sendRetry--; @@ -625,7 +413,8 @@ void PzemEvery200ms() bool PzemInit() { - return PzemSerial(pin[GPIO_PZEM_RX], pin[GPIO_PZEM_TX]); + PzemSerial = new TasmotaSerial(pin[GPIO_PZEM_RX], pin[GPIO_PZEM_TX]); + return PzemSerial->begin(); } /********************************************************************************************/ @@ -677,14 +466,14 @@ void Energy200ms() #endif // USE_PZEM004T } - if (energy_power_factor_ready && energy_voltage && energy_current && energy_power) { - energy_power_factor_ready = false; - float power_factor = energy_power / (energy_voltage * energy_current); + float power_factor = 0; + if (energy_voltage && energy_current && energy_power) { + power_factor = energy_power / (energy_voltage * energy_current); if (power_factor > 1) { power_factor = 1; } - energy_power_factor = power_factor; } + energy_power_factor = power_factor; } void EnergySaveState() @@ -1144,23 +933,21 @@ boolean Xsns03(byte function) if (energy_flg) { switch (function) { - case FUNC_XSNS_INIT: + case FUNC_INIT: EnergyInit(); break; - case FUNC_XSNS_EVERY_SECOND: + case FUNC_EVERY_SECOND: EnergyMarginCheck(); break; -// case FUNC_XSNS_PREP: -// break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: EnergyShow(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: EnergyShow(0); break; #endif // USE_WEBSERVER - case FUNC_XSNS_SAVE_STATE: + case FUNC_SAVE_BEFORE_RESTART: EnergySaveState(); break; } diff --git a/sonoff/xsns_04_snfsc.ino b/sonoff/xsns_04_snfsc.ino index fbe2a2e8a..f6e44eae5 100644 --- a/sonoff/xsns_04_snfsc.ino +++ b/sonoff/xsns_04_snfsc.ino @@ -1,7 +1,7 @@ /* xsns_04_snfsc.ino - sonoff SC 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 @@ -148,16 +148,14 @@ boolean Xsns04(byte function) if (SONOFF_SC == Settings.module) { switch (function) { - case FUNC_XSNS_INIT: + case FUNC_INIT: SonoffScInit(); break; -// case FUNC_XSNS_PREP: -// break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: SonoffScShow(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: SonoffScShow(0); break; #endif // USE_WEBSERVER diff --git a/sonoff/xsns_05_ds18b20.ino b/sonoff/xsns_05_ds18b20.ino index 5c9ff013d..4a4220831 100644 --- a/sonoff/xsns_05_ds18b20.ino +++ b/sonoff/xsns_05_ds18b20.ino @@ -1,7 +1,7 @@ /* xsns_05_ds18b20.ino - DS18B20 temperature 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 @@ -219,17 +219,17 @@ boolean Xsns05(byte function) if (pin[GPIO_DSB] < 99) { switch (function) { - case FUNC_XSNS_INIT: + case FUNC_INIT: Ds18x20Init(); break; - case FUNC_XSNS_PREP: + case FUNC_PREP_BEFORE_TELEPERIOD: Ds18x20Convert(); // Start conversion, takes up to one second break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: Ds18b20Show(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: Ds18b20Show(0); Ds18x20Convert(); // Start conversion, takes up to one second break; diff --git a/sonoff/xsns_05_ds18x20.ino b/sonoff/xsns_05_ds18x20.ino index de334b4d2..c8ed8be59 100644 --- a/sonoff/xsns_05_ds18x20.ino +++ b/sonoff/xsns_05_ds18x20.ino @@ -1,7 +1,7 @@ /* xsns_05_ds18x20.ino - DS18x20 temperature 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 @@ -401,17 +401,17 @@ boolean Xsns05(byte function) if (pin[GPIO_DSB] < 99) { switch (function) { - case FUNC_XSNS_INIT: + case FUNC_INIT: Ds18x20Init(); break; - case FUNC_XSNS_PREP: + case FUNC_PREP_BEFORE_TELEPERIOD: Ds18x20Convert(); // Start conversion, takes up to one second break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: Ds18x20Show(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: Ds18x20Show(0); Ds18x20Convert(); // Start conversion, takes up to one second break; diff --git a/sonoff/xsns_05_ds18x20_legacy.ino b/sonoff/xsns_05_ds18x20_legacy.ino index beffe780d..881337656 100644 --- a/sonoff/xsns_05_ds18x20_legacy.ino +++ b/sonoff/xsns_05_ds18x20_legacy.ino @@ -1,7 +1,7 @@ /* xsns_05_ds18x20_legacy.ino - DS18x20 temperature sensor support for Sonoff-Tasmota - Copyright (C) 2017 Heiko Krupp and Theo Arends + Copyright (C) 2018 Heiko Krupp 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 @@ -219,18 +219,18 @@ boolean Xsns05(byte function) if (pin[GPIO_DSB] < 99) { switch (function) { - case FUNC_XSNS_INIT: + case FUNC_INIT: Ds18x20Init(); break; - case FUNC_XSNS_PREP: + case FUNC_PREP_BEFORE_TELEPERIOD: Ds18x20Search(); // Check for changes in sensors number Ds18x20Convert(); // Start Conversion, takes up to one second break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: Ds18x20Show(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: Ds18x20Show(0); break; #endif // USE_WEBSERVER diff --git a/sonoff/xsns_06_dht.ino b/sonoff/xsns_06_dht.ino index 8af52efe1..620b57cd5 100644 --- a/sonoff/xsns_06_dht.ino +++ b/sonoff/xsns_06_dht.ino @@ -1,7 +1,7 @@ /* xsns_06_dht.ino - DHTxx, AM23xx and SI7021 temperature and humidity 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 @@ -253,17 +253,17 @@ boolean Xsns06(byte function) if (dht_flg) { switch (function) { - case FUNC_XSNS_INIT: + case FUNC_INIT: DhtInit(); break; - case FUNC_XSNS_PREP: + case FUNC_PREP_BEFORE_TELEPERIOD: DhtReadPrep(); break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: DhtShow(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: DhtShow(0); break; #endif // USE_WEBSERVER diff --git a/sonoff/xsns_07_sht1x.ino b/sonoff/xsns_07_sht1x.ino index db1ad3717..6ff557aff 100644 --- a/sonoff/xsns_07_sht1x.ino +++ b/sonoff/xsns_07_sht1x.ino @@ -1,7 +1,7 @@ /* xsns_07_sht1x.ino - SHT1x temperature and 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 @@ -223,16 +223,14 @@ boolean Xsns07(byte function) if (i2c_flg) { switch (function) { -// case FUNC_XSNS_INIT: -// break; - case FUNC_XSNS_PREP: + case FUNC_PREP_BEFORE_TELEPERIOD: ShtDetect(); break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: ShtShow(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: ShtShow(0); break; #endif // USE_WEBSERVER diff --git a/sonoff/xsns_08_htu21.ino b/sonoff/xsns_08_htu21.ino index 6a1053145..d19453e4d 100644 --- a/sonoff/xsns_08_htu21.ino +++ b/sonoff/xsns_08_htu21.ino @@ -1,7 +1,7 @@ /* xsns_08_htu21.ino - HTU21 temperature and humidity sensor support for Sonoff-Tasmota - Copyright (C) 2017 Heiko Krupp and Theo Arends + Copyright (C) 2018 Heiko Krupp 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 @@ -284,16 +284,14 @@ boolean Xsns08(byte function) if (i2c_flg) { switch (function) { -// case FUNC_XSNS_INIT: -// break; - case FUNC_XSNS_PREP: + case FUNC_PREP_BEFORE_TELEPERIOD: HtuDetect(); break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: HtuShow(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: HtuShow(0); break; #endif // USE_WEBSERVER diff --git a/sonoff/xsns_09_bmp.ino b/sonoff/xsns_09_bmp.ino index 742fa71c3..0653d0c0d 100644 --- a/sonoff/xsns_09_bmp.ino +++ b/sonoff/xsns_09_bmp.ino @@ -1,7 +1,7 @@ /* xsns_09_bmp.ino - BMP pressure, temperature, humidity and gas sensor support for Sonoff-Tasmota - Copyright (C) 2017 Heiko Krupp and Theo Arends + Copyright (C) 2018 Heiko Krupp 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 @@ -502,19 +502,17 @@ boolean Xsns09(byte function) if (i2c_flg) { switch (function) { -// case FUNC_XSNS_INIT: -// break; - case FUNC_XSNS_PREP: + case FUNC_PREP_BEFORE_TELEPERIOD: BmpDetect(); #ifdef USE_BME680 Bme680PerformReading(); #endif // USE_BME680 break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: BmpShow(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: BmpShow(0); #ifdef USE_BME680 Bme680PerformReading(); diff --git a/sonoff/xsns_10_bh1750.ino b/sonoff/xsns_10_bh1750.ino index 7653e2beb..fc05673d0 100644 --- a/sonoff/xsns_10_bh1750.ino +++ b/sonoff/xsns_10_bh1750.ino @@ -1,7 +1,7 @@ /* xsns_10_bh1750.ino - BH1750 ambient light 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 @@ -99,16 +99,14 @@ boolean Xsns10(byte function) if (i2c_flg) { switch (function) { -// case FUNC_XSNS_INIT: -// break; - case FUNC_XSNS_PREP: + case FUNC_PREP_BEFORE_TELEPERIOD: Bh1750Detect(); break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: Bh1750Show(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: Bh1750Show(0); break; #endif // USE_WEBSERVER diff --git a/sonoff/xsns_11_veml6070.ino b/sonoff/xsns_11_veml6070.ino index b6d680d45..6b27de71e 100644 --- a/sonoff/xsns_11_veml6070.ino +++ b/sonoff/xsns_11_veml6070.ino @@ -1,7 +1,7 @@ /* xsns_11_veml6070.ino - VEML6070 ultra violet light 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 @@ -104,16 +104,14 @@ boolean Xsns11(byte function) if (i2c_flg) { switch (function) { -// case FUNC_XSNS_INIT: -// break; - case FUNC_XSNS_PREP: + case FUNC_PREP_BEFORE_TELEPERIOD: Veml6070Detect(); break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: Veml6070Show(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: Veml6070Show(0); break; #endif // USE_WEBSERVER diff --git a/sonoff/xsns_12_ads1115.ino b/sonoff/xsns_12_ads1115.ino index 132d2725d..4c2e7f976 100644 --- a/sonoff/xsns_12_ads1115.ino +++ b/sonoff/xsns_12_ads1115.ino @@ -1,7 +1,7 @@ /* xsns_12_ads1115_ada.ino - ADS1115 A/D Converter 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 @@ -218,16 +218,14 @@ boolean Xsns12(byte function) if (i2c_flg) { switch (function) { -// case FUNC_XSNS_INIT: -// break; - case FUNC_XSNS_PREP: + case FUNC_PREP_BEFORE_TELEPERIOD: Ads1115Detect(); break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: Ads1115Show(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: Ads1115Show(0); break; #endif // USE_WEBSERVER diff --git a/sonoff/xsns_12_ads1115_i2cdev.ino b/sonoff/xsns_12_ads1115_i2cdev.ino index 1da6c62e6..d9735327a 100644 --- a/sonoff/xsns_12_ads1115_i2cdev.ino +++ b/sonoff/xsns_12_ads1115_i2cdev.ino @@ -1,7 +1,7 @@ /* xsns_12_ads1115.ino - ADS1x15 A/D Converter support for Sonoff-Tasmota - Copyright (C) 2017 Stefan Bode and Theo Arends + Copyright (C) 2018 Stefan Bode 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 @@ -138,16 +138,14 @@ boolean Xsns12(byte function) if (i2c_flg) { switch (function) { -// case FUNC_XSNS_INIT: -// break; - case FUNC_XSNS_PREP: + case FUNC_PREP_BEFORE_TELEPERIOD: Ads1115Detect(); break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: Ads1115Show(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: Ads1115Show(0); break; #endif // USE_WEBSERVER diff --git a/sonoff/xsns_13_ina219.ino b/sonoff/xsns_13_ina219.ino index 169e51426..211021d9e 100644 --- a/sonoff/xsns_13_ina219.ino +++ b/sonoff/xsns_13_ina219.ino @@ -1,7 +1,7 @@ /* xsns_13_ina219.ino - INA219 Current Sensor support for Sonoff-Tasmota - Copyright (C) 2017 Stefan Bode and Theo Arends + Copyright (C) 2018 Stefan Bode 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 @@ -219,16 +219,14 @@ boolean Xsns13(byte function) if (i2c_flg) { switch (function) { -// case FUNC_XSNS_INIT: -// break; - case FUNC_XSNS_PREP: + case FUNC_PREP_BEFORE_TELEPERIOD: Ina219Detect(); break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: Ina219Show(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: Ina219Show(0); break; #endif // USE_WEBSERVER diff --git a/sonoff/xsns_14_sht3x.ino b/sonoff/xsns_14_sht3x.ino index 420e8d078..39b24d8b4 100644 --- a/sonoff/xsns_14_sht3x.ino +++ b/sonoff/xsns_14_sht3x.ino @@ -1,7 +1,7 @@ /* xsns_14_sht3x.ino - SHT3X temperature and humidity 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 @@ -125,17 +125,17 @@ boolean Xsns14(byte function) if (i2c_flg) { switch (function) { - case FUNC_XSNS_INIT: + case FUNC_INIT: Sht3xDetect(); break; - case FUNC_XSNS_PREP: + case FUNC_PREP_BEFORE_TELEPERIOD: Sht3xConvert(); break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: Sht3xShow(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: Sht3xShow(0); Sht3xConvert(); break; diff --git a/sonoff/xsns_14_sht3x_v2.ino b/sonoff/xsns_14_sht3x_v2.ino index 5329b373b..85de8512a 100644 --- a/sonoff/xsns_14_sht3x_v2.ino +++ b/sonoff/xsns_14_sht3x_v2.ino @@ -1,7 +1,7 @@ /* xsns_14_sht3x.ino - SHT3X temperature and humidity 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 @@ -122,16 +122,14 @@ boolean Xsns14(byte function) if (i2c_flg) { switch (function) { - case FUNC_XSNS_INIT: + case FUNC_INIT: Sht3xDetect(); break; -// case FUNC_XSNS_PREP: -// break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: Sht3xShow(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: Sht3xShow(0); break; #endif // USE_WEBSERVER diff --git a/sonoff/xsns_14_sht3x_v3.ino b/sonoff/xsns_14_sht3x_v3.ino index cfc933afb..aff368c1a 100644 --- a/sonoff/xsns_14_sht3x_v3.ino +++ b/sonoff/xsns_14_sht3x_v3.ino @@ -1,7 +1,7 @@ /* xsns_14_sht3x.ino - SHT3X temperature and humidity 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 @@ -132,17 +132,17 @@ boolean Xsns14(byte function) if (i2c_flg) { switch (function) { - case FUNC_XSNS_INIT: + case FUNC_INIT: Sht3xDetect(); break; - case FUNC_XSNS_PREP: + case FUNC_PREP_BEFORE_TELEPERIOD: Sht3xConvert(); break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: Sht3xShow(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: Sht3xShow(0); Sht3xConvert(); break; diff --git a/sonoff/xsns_15_mhz19.ino b/sonoff/xsns_15_mhz19.ino new file mode 100644 index 000000000..7f5e56aa8 --- /dev/null +++ b/sonoff/xsns_15_mhz19.ino @@ -0,0 +1,265 @@ +/* + xsns_15_mhz19.ino - MH-Z19(B) CO2 sensor support for Sonoff-Tasmota + + 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 + 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 +/*********************************************************************************************\ + * MH-Z19 - CO2 sensor + * + * Adapted from EspEasy plugin P049 by Dmitry (rel22 ___ inbox.ru) + ********************************************************************************************** + * Filter usage + * + * Select filter usage on low stability readings +\*********************************************************************************************/ + +enum MhzFilterOptions {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)) + +/*********************************************************************************************/ + +#include + +TasmotaSerial *MhzSerial; + +#define MHZ19_READ_TIMEOUT 500 // Must be way less than 1000 +#define MHZ19_RETRY_COUNT 8 + +const char kMhzTypes[] PROGMEM = "MHZ19|MHZ19B"; + +const uint8_t mhz_cmnd_read_ppm[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; +const uint8_t mhz_cmnd_abc_enable[9] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6}; +const uint8_t mhz_cmnd_abc_disable[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86}; + +uint8_t mhz_type = 1; +uint16_t mhz_last_ppm = 0; +uint8_t mhz_filter = MHZ19_FILTER_OPTION; +bool mhz_abc_enable = MHZ19_ABC_ENABLE; +bool mhz_abc_must_apply = false; +char mhz_types[7]; + +float mhz_temperature = 0; +uint8_t mhz_timer = 0; +uint8_t mhz_retry = MHZ19_RETRY_COUNT; + +uint8_t mhz_state = 0; + +/*********************************************************************************************/ + +bool MhzCheckAndApplyFilter(uint16_t ppm, uint8_t s) +{ + if (1 == s) { + return false; // S==1 => "A" version sensor bootup, do not use values. + } + if (mhz_last_ppm < 400 || mhz_last_ppm > 5000) { + // Prevent unrealistic values during start-up with filtering enabled. + // Just assume the entered value is correct. + mhz_last_ppm = ppm; + return true; + } + int32_t difference = ppm - mhz_last_ppm; + if (s > 0 && s < 64 && mhz_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 *= s; + difference /= 64; + } + if (MHZ19_FILTER_OFF == mhz_filter) { + if (s != 0 && s != 64) { + return false; + } + } else { + difference >>= (mhz_filter -1); + } + mhz_last_ppm = static_cast(mhz_last_ppm + difference); + return true; +} + +void Mhz50ms() +{ + mhz_state++; + if (4 == mhz_state) { // Every 200 mSec + mhz_state = 0; + + uint8_t mhz_response[9]; + + mhz_timer++; + if (6 == mhz_timer) { // MH-Z19 measuring cycle takes 1005 +5% ms + mhz_timer = 0; + + MhzSerial->write(mhz_cmnd_read_ppm, 9); + } + + if (1 == mhz_timer) { + if (mhz_retry) { + mhz_retry--; + if (!mhz_retry) { + mhz_last_ppm = 0; + mhz_temperature = 0; + } + } + + unsigned long start = millis(); + uint8_t counter = 0; + while (((millis() - start) < MHZ19_READ_TIMEOUT) && (counter < 9)) { + if (MhzSerial->available() > 0) { + mhz_response[counter++] = MhzSerial->read(); + } + } + if (counter < 9) { +// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 comms timeout")); + return; + } + + byte crc = 0; + for (uint8_t i = 1; i < 8; i++) { + crc += mhz_response[i]; + } + crc = 255 - crc; + crc++; + if (mhz_response[8] != crc) { +// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 crc error")); + return; + } + if (0xFF != mhz_response[0] || 0x86 != mhz_response[1]) { +// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 bad response")); + return; + } + + uint16_t u = (mhz_response[6] << 8) | mhz_response[7]; + if (15000 == u) { // During (and only ever at) sensor boot, 'u' is reported as 15000 + if (!mhz_abc_enable) { + // After bootup of the sensor the ABC will be enabled. + // Thus only actively disable after bootup. + mhz_abc_must_apply = true; + } + } else { + uint16_t ppm = (mhz_response[2] << 8) | mhz_response[3]; + mhz_temperature = ConvertTemp((float)mhz_response[4] - 40); + uint8_t s = mhz_response[5]; + mhz_type = (s) ? 1 : 2; + if (MhzCheckAndApplyFilter(ppm, s)) { + mhz_retry = MHZ19_RETRY_COUNT; + + if (0 == s || 64 == s) { // Reading is stable. + if (mhz_abc_must_apply) { + mhz_abc_must_apply = false; + if (mhz_abc_enable) { + MhzSerial->write(mhz_cmnd_abc_enable, 9); // Sent sensor ABC Enable + } else { + MhzSerial->write(mhz_cmnd_abc_disable, 9); // Sent sensor ABC Disable + } + } + } + + } + } + } + + } +} + +/*********************************************************************************************/ + +void MhzInit() +{ + mhz_type = 0; + if ((pin[GPIO_MHZ_RXD] < 99) && (pin[GPIO_MHZ_TXD] < 99)) { + MhzSerial = new TasmotaSerial(pin[GPIO_MHZ_RXD], pin[GPIO_MHZ_TXD]); + if (MhzSerial->begin()) { + mhz_type = 1; + } + } +} + +void MhzShow(boolean json) +{ + char temperature[10]; + dtostrfd(mhz_temperature, Settings.flag2.temperature_resolution, temperature); + GetTextIndexed(mhz_types, sizeof(mhz_types), mhz_type -1, kMhzTypes); + + if (json) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_CO2 "\":%d,\"" D_TEMPERATURE "\":%s}"), mqtt_data, mhz_types, mhz_last_ppm, temperature); +#ifdef USE_DOMOTICZ + DomoticzSensor(DZ_AIRQUALITY, mhz_last_ppm); +#endif // USE_DOMOTICZ +#ifdef USE_WEBSERVER + } else { + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_CO2, mqtt_data, mhz_types, mhz_last_ppm); + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, mhz_types, temperature, TempUnit()); +#endif // USE_WEBSERVER + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +#define XSNS_15 + +boolean Xsns15(byte function) +{ + boolean result = false; + + if (mhz_type) { + switch (function) { + case FUNC_INIT: + MhzInit(); + break; + case FUNC_EVERY_50_MSECOND: + Mhz50ms(); + break; + case FUNC_JSON_APPEND: + MhzShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_APPEND: + MhzShow(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_MHZ19 diff --git a/sonoff/xsns_15_mhz_hardserial.ino b/sonoff/xsns_15_mhz_hardserial.ino deleted file mode 100644 index cde575f2d..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: -// Mhz19Prep(); - break; - case FUNC_XSNS_JSON_APPEND: - Mhz19Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: - Mhz19Show(0); -// Mhz19Prep(); - break; -#endif // USE_WEBSERVER - } - } - return result; -} - -#endif // USE_MHZ19_HARD_SERIAL diff --git a/sonoff/xsns_15_mhz_softserial.ino b/sonoff/xsns_15_mhz_softserial.ino deleted file mode 100644 index b77faeb20..000000000 --- a/sonoff/xsns_15_mhz_softserial.ino +++ /dev/null @@ -1,377 +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 -/*********************************************************************************************\ - * MH-Z19 - CO2 sensor - * - * 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 uint8_t mhz19_cmnd_read_ppm[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; -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; -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]; - -/*********************************************************************************************\ - * Subset SoftwareSerial -\*********************************************************************************************/ - -#define MHZ19_SERIAL_BUFFER_SIZE 20 -#define MHZ19_SERIAL_WAIT { while (ESP.getCycleCount() -start < wait) optimistic_yield(1); wait += mhz19_serial_bit_time; } - -uint8_t mhz19_serial_rx_pin; -uint8_t mhz19_serial_tx_pin; -uint8_t mhz19_serial_in_pos = 0; -uint8_t mhz19_serial_out_pos = 0; -uint8_t mhz19_serial_buffer[MHZ19_SERIAL_BUFFER_SIZE]; -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); -} - -bool Mhz19Serial(uint8_t receive_pin, uint8_t transmit_pin) -{ - if (!((Mhz19SerialValidGpioPin(receive_pin)) && (Mhz19SerialValidGpioPin(transmit_pin) || transmit_pin == 16))) { - return false; - } - mhz19_serial_rx_pin = receive_pin; - pinMode(mhz19_serial_rx_pin, INPUT); - attachInterrupt(mhz19_serial_rx_pin, Mhz19SerialRxRead, FALLING); - - mhz19_serial_tx_pin = transmit_pin; - pinMode(mhz19_serial_tx_pin, OUTPUT); - digitalWrite(mhz19_serial_tx_pin, 1); - - mhz19_serial_bit_time = ESP.getCpuFreqMHz() *1000000 /MHZ19_BAUDRATE; // 8333 - mhz19_serial_bit_time_start = mhz19_serial_bit_time + mhz19_serial_bit_time /3 -500; // 10610 ICACHE_RAM_ATTR start delay -// mhz19_serial_bit_time_start = mhz19_serial_bit_time; // Non ICACHE_RAM_ATTR start delay (experimental) - - return true; -} - -int Mhz19SerialRead() { - if (mhz19_serial_in_pos == mhz19_serial_out_pos) { - return -1; - } - int ch = mhz19_serial_buffer[mhz19_serial_out_pos]; - mhz19_serial_out_pos = (mhz19_serial_out_pos +1) % MHZ19_SERIAL_BUFFER_SIZE; - return ch; -} - -int Mhz19SerialAvailable() { - int avail = mhz19_serial_in_pos - mhz19_serial_out_pos; - if (avail < 0) { - avail += MHZ19_SERIAL_BUFFER_SIZE; - } - return avail; -} - -void Mhz19SerialFlush() -{ - mhz19_serial_in_pos = 0; - mhz19_serial_out_pos = 0; -} - -size_t Mhz19SerialTxWrite(uint8_t b) -{ - unsigned long wait = mhz19_serial_bit_time; - digitalWrite(mhz19_serial_tx_pin, HIGH); - unsigned long start = ESP.getCycleCount(); - // Start bit; - digitalWrite(mhz19_serial_tx_pin, LOW); - MHZ19_SERIAL_WAIT; - for (int i = 0; i < 8; i++) { - digitalWrite(mhz19_serial_tx_pin, (b & 1) ? HIGH : LOW); - MHZ19_SERIAL_WAIT; - b >>= 1; - } - // Stop bit - digitalWrite(mhz19_serial_tx_pin, HIGH); - MHZ19_SERIAL_WAIT; - return 1; -} - -size_t Mhz19SerialWrite(const uint8_t *buffer, size_t size = 1) { - size_t n = 0; - while(size--) { - n += Mhz19SerialTxWrite(*buffer++); - } - return n; -} - -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 - unsigned long wait = mhz19_serial_bit_time_start; - unsigned long start = ESP.getCycleCount(); - uint8_t rec = 0; - for (int i = 0; i < 8; i++) { - MHZ19_SERIAL_WAIT; - rec >>= 1; - if (digitalRead(mhz19_serial_rx_pin)) { - rec |= 0x80; - } - } - // Stop bit - MHZ19_SERIAL_WAIT; - // Store the received value in the buffer unless we have an overflow - int next = (mhz19_serial_in_pos +1) % MHZ19_SERIAL_BUFFER_SIZE; - if (next != mhz19_serial_out_pos) { - mhz19_serial_buffer[mhz19_serial_in_pos] = rec; - mhz19_serial_in_pos = next; - } - // Must clear this bit in the interrupt register, - // it gets set even when interrupts are disabled - GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << mhz19_serial_rx_pin); -} - -/*********************************************************************************************/ - -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 *= s; - difference /= 64; - } - if (MHZ19_FILTER_OFF == mhz19_filter) { - if (s != 0 && s != 64) { - return false; - } - } else { - difference >>= (mhz19_filter -1); - } - 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) - { - 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(); - 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 - } - - 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) { - 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; - } -} - -#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: -// Mhz19Prep(); - break; - case FUNC_XSNS_JSON_APPEND: - Mhz19Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: - Mhz19Show(0); -// Mhz19Prep(); - break; -#endif // USE_WEBSERVER - } - } - return result; -} - -#endif // USE_MHZ19_SOFT_SERIAL diff --git a/sonoff/xsns_15_mhz_softserial_obsolete.ino b/sonoff/xsns_15_mhz_softserial_obsolete.ino deleted file mode 100644 index 4f468b17e..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: -// Mhz19Prep(); - break; - case FUNC_XSNS_JSON_APPEND: - Mhz19Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: - Mhz19Show(0); -// Mhz19Prep(); - break; -#endif // USE_WEBSERVER - } - } - return result; -} - -#endif // USE_MHZ19_SOFT_SERIAL_OBSOLETE diff --git a/sonoff/xsns_16_tsl2561.ino b/sonoff/xsns_16_tsl2561.ino index 24c9468fd..cbddb7e8e 100644 --- a/sonoff/xsns_16_tsl2561.ino +++ b/sonoff/xsns_16_tsl2561.ino @@ -1,7 +1,7 @@ /* xsns_16_tsl2561.ino - TSL2561 light 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 @@ -91,16 +91,14 @@ boolean Xsns16(byte function) if (i2c_flg) { switch (function) { -// case FUNC_XSNS_INIT: -// break; - case FUNC_XSNS_PREP: + case FUNC_PREP_BEFORE_TELEPERIOD: Tsl2561Detect(); break; - case FUNC_XSNS_JSON_APPEND: + case FUNC_JSON_APPEND: Tsl2561Show(1); break; #ifdef USE_WEBSERVER - case FUNC_XSNS_WEB: + case FUNC_WEB_APPEND: Tsl2561Show(0); break; #endif // USE_WEBSERVER diff --git a/sonoff/xsns_17_senseair.ino b/sonoff/xsns_17_senseair.ino new file mode 100644 index 000000000..c3457af18 --- /dev/null +++ b/sonoff/xsns_17_senseair.ino @@ -0,0 +1,248 @@ +/* + xsns_17_senseair.ino - SenseAir CO2 sensor support for Sonoff-Tasmota + + 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 + 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_SENSEAIR +/*********************************************************************************************\ + * SenseAir K30, K70 and S8 - CO2 sensor + * + * Adapted from EspEasy plugin P052 by Mikael Trieb (mikael__AT__triebconsulting.se) +\*********************************************************************************************/ + +#include + +TasmotaSerial *SensairSerial; + +const char kSenseairTypes[] PROGMEM = "Kx0|S8"; + +uint8_t senseair_type = 1; +char senseair_types[7]; + +uint16_t senseair_co2 = 0; +float senseair_temperature = 0; +float senseair_humidity = 0; + +uint8_t senseair_state = 0; + +/*********************************************************************************************/ + +void ModbusSend(uint8_t function_code, uint16_t start_address, uint16_t register_count) +{ + uint8_t frame[8]; + + frame[0] = 0xFE; // Any Address + frame[1] = function_code; + frame[2] = (uint8_t)(start_address >> 8); + frame[3] = (uint8_t)(start_address); + frame[4] = (uint8_t)(register_count >> 8); + frame[5] = (uint8_t)(register_count); + uint16_t crc = 0xFFFF; + for (uint8_t pos = 0; pos < sizeof(frame) -2; pos++) { + crc ^= (uint16_t)frame[pos]; // XOR byte into least sig. byte of crc + for (uint8_t i = 8; i != 0; i--) { // Loop over each bit + if ((crc & 0x0001) != 0) { // If the LSB is set + crc >>= 1; // Shift right and XOR 0xA001 + crc ^= 0xA001; + } + else { // Else LSB is not set + crc >>= 1; // Just shift right + } + } + } + frame[7] = (uint8_t)((crc >> 8) & 0xFF); + frame[6] = (uint8_t)(crc & 0xFF); + + SensairSerial->write(frame, sizeof(frame)); +} + +bool ModbusReceiveReady() +{ + return (SensairSerial->available() >= 5); // 5 - Error frame, 7 - Ok frame +} + +uint8_t ModbusReceive(uint16_t *value) +{ + uint8_t buffer[7]; + + uint8_t len = 0; + while (SensairSerial->available() > 0) { + buffer[len++] = (uint8_t)SensairSerial->read(); + if (3 == len) { + if (buffer[1] & 0x80) { // fe 84 02 f2 f1 + return buffer[2]; // 1 = Illegal Function, 2 = Illegal Data Address, 3 = Illegal Data Value + } + } + } + if (len != sizeof(buffer)) { + return 9; // 9 = Unexpected result + } + *value = (buffer[3] << 8) | buffer[4]; + return 0; // 0 = No error +} + +/*********************************************************************************************/ + +const uint8_t start_addresses[] { 0x1A, 0x00, 0x03, 0x04, 0x05, 0x1C, 0x0A }; + +uint8_t senseair_read_state = 0; +uint8_t senseair_send_retry = 0; + +void Senseair50ms() // Every 50 mSec +{ + senseair_state++; + if (6 == senseair_state) { // Every 300 mSec + senseair_state = 0; + + uint16_t value = 0; + bool data_ready = ModbusReceiveReady(); + + if (data_ready) { + uint8_t error = ModbusReceive(&value); + if (error) { + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SenseAir response error %d"), error); + AddLog(LOG_LEVEL_DEBUG); + } else { + switch(senseair_read_state) { + case 0: // 0x1A (26) READ_TYPE_LOW - S8: fe 04 02 01 77 ec 92 + senseair_type = 2; + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SenseAir type id low %04X"), value); + AddLog(LOG_LEVEL_DEBUG); + break; + case 1: // 0x00 (0) READ_ERRORLOG - fe 04 02 00 00 ad 24 + if (value) { + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SenseAir error %04X"), value); + AddLog(LOG_LEVEL_DEBUG); + } + break; + case 2: // 0x03 (3) READ_CO2 - fe 04 02 06 2c af 59 + senseair_co2 = value; + break; + case 3: // 0x04 (4) READ_TEMPERATURE - S8: fe 84 02 f2 f1 - Illegal Data Address + senseair_temperature = ConvertTemp((float)value / 100); + break; + case 4: // 0x05 (5) READ_HUMIDITY - S8: fe 84 02 f2 f1 - Illegal Data Address + senseair_humidity = (float)value / 100; + break; + case 5: // 0x1C (28) READ_RELAY_STATE - S8: fe 04 02 01 54 ad 4b - firmware version + { + bool relay_state = value >> 8 & 1; + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SenseAir relay state %d"), relay_state); + AddLog(LOG_LEVEL_DEBUG); + break; + } + case 6: // 0x0A (10) READ_TEMP_ADJUSTMENT - S8: fe 84 02 f2 f1 - Illegal Data Address + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SenseAir temp adjustment %d"), value); + AddLog(LOG_LEVEL_DEBUG); + break; + } + } + senseair_read_state++; + if (2 == senseair_type) { // S8 + if (3 == senseair_read_state) { + senseair_read_state = 1; + } + } else { // K30, K70 + if (sizeof(start_addresses) == senseair_read_state) { + senseair_read_state = 1; + } + } + } + + if (0 == senseair_send_retry || data_ready) { + senseair_send_retry = 5; + ModbusSend(0x04, (uint16_t)start_addresses[senseair_read_state], 1); + } else { + senseair_send_retry--; + } + + } +} + +/*********************************************************************************************/ + +void SenseairInit() +{ + senseair_type = 0; + if ((pin[GPIO_SAIR_RX] < 99) && (pin[GPIO_SAIR_TX] < 99)) { + SensairSerial = new TasmotaSerial(pin[GPIO_SAIR_RX], pin[GPIO_SAIR_TX]); + if (SensairSerial->begin()) { + senseair_type = 1; + } + } +} + +void SenseairShow(boolean json) +{ + char temperature[10]; + char humidity[10]; + dtostrfd(senseair_temperature, Settings.flag2.temperature_resolution, temperature); + dtostrfd(senseair_humidity, Settings.flag2.temperature_resolution, humidity); + GetTextIndexed(senseair_types, sizeof(senseair_types), senseair_type -1, kSenseairTypes); + + if (json) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_CO2 "\":%d"), mqtt_data, senseair_types, senseair_co2); + if (senseair_type != 2) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_TEMPERATURE "\":%s,\"" D_HUMIDITY "\":%s"), mqtt_data, temperature, humidity); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); +#ifdef USE_DOMOTICZ + DomoticzSensor(DZ_AIRQUALITY, senseair_co2); +#endif // USE_DOMOTICZ +#ifdef USE_WEBSERVER + } else { + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_CO2, mqtt_data, senseair_types, senseair_co2); + if (senseair_type != 2) { + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, senseair_types, temperature, TempUnit()); + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_HUM, mqtt_data, senseair_types, humidity); + } +#endif // USE_WEBSERVER + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +#define XSNS_17 + +boolean Xsns17(byte function) +{ + boolean result = false; + + if (senseair_type) { + switch (function) { + case FUNC_INIT: + SenseairInit(); + break; + case FUNC_EVERY_50_MSECOND: + Senseair50ms(); + break; + case FUNC_JSON_APPEND: + SenseairShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_APPEND: + SenseairShow(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_SENSEAIR diff --git a/sonoff/xsns_interface.ino b/sonoff/xsns_interface.ino index 7ef5d8199..2893e3f51 100644 --- a/sonoff/xsns_interface.ino +++ b/sonoff/xsns_interface.ino @@ -1,7 +1,7 @@ /* xsns_interface.ino - External sensor interface support for Sonoff-Tasmota - Copyright (C) 2017 Theo Arends inspired by ESPEasy + Copyright (C) 2018 Theo Arends inspired by ESPEasy 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 @@ -104,7 +104,10 @@ void XSnsInit() xsns_func_ptr[xsns_present++] = &Xsns20; #endif - XsnsCall(FUNC_XSNS_INIT); +// snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "Sensors found %d"), xsns_present); +// AddLog(LOG_LEVEL_DEBUG); + + XsnsCall(FUNC_INIT); } /*********************************************************************************************\ @@ -115,18 +118,23 @@ boolean XsnsCall(byte Function) { boolean result = false; +/* switch (Function) { - case FUNC_XSNS_INIT: - case FUNC_XSNS_EVERY_SECOND: - case FUNC_XSNS_PREP: - case FUNC_XSNS_JSON_APPEND: - case FUNC_XSNS_WEB: - case FUNC_XSNS_SAVE_STATE: + case FUNC_INIT: + case FUNC_EVERY_50_MSECOND: + case FUNC_EVERY_SECOND: + case FUNC_PREP_BEFORE_TELEPERIOD: + case FUNC_JSON_APPEND: + case FUNC_WEB_APPEND: + case FUNC_SAVE_BEFORE_RESTART: +*/ for (byte x = 0; x < xsns_present; x++) { xsns_func_ptr[x](Function); } +/* break; } +*/ return result; }