From 0747e0fc68542a062ba58233adf2291d0a66efc1 Mon Sep 17 00:00:00 2001 From: arendst Date: Fri, 8 Dec 2017 14:14:10 +0100 Subject: [PATCH] Updated MH-Z19 drivers 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) * Change ADS1115 default voltage range from +/-2V to +/-6V (#1289) * Add multipress support and more user configurable options to Sonoff Dual R2 (#1291) * Fix Sonoff Bridge missed learned key if learned data contains 0x55 (End of Transmission) flag (#1095, #1294) * Add support for TSL2561 using adafruit library (#661, #1311) * Add alternative support for SHT3x (#1314) --- lib/EspSoftwareSerial-3.3.1/README.md | 11 - .../SoftwareSerial.cpp | 228 -------------- lib/EspSoftwareSerial-3.3.1/SoftwareSerial.h | 88 ------ .../examples/swsertest/swsertest.ino | 27 -- lib/EspSoftwareSerial-3.3.1/keywords.txt | 31 -- lib/EspSoftwareSerial-3.3.1/library.json | 15 - .../library.properties | 9 - lib/EspSoftwareSerialNoIram-3.3.1/README.md | 11 - .../SoftwareSerialNoIram.cpp | 248 ---------------- .../SoftwareSerialNoIram.h | 88 ------ .../examples/swsertest/swsertest.ino | 27 -- .../keywords.txt | 31 -- .../library.json | 15 - .../library.properties | 9 - sonoff/_releasenotes.ino | 5 +- sonoff/user_config.h | 14 +- sonoff/xsns_15_mhz_hardserial.ino | 277 ++++++++++++++++++ sonoff/xsns_15_mhz_softserial.ino | 182 +++++++++--- sonoff/xsns_15_mhz_softserial_obsolete.ino | 277 ++++++++++++++++++ 19 files changed, 701 insertions(+), 892 deletions(-) delete mode 100644 lib/EspSoftwareSerial-3.3.1/README.md delete mode 100644 lib/EspSoftwareSerial-3.3.1/SoftwareSerial.cpp delete mode 100644 lib/EspSoftwareSerial-3.3.1/SoftwareSerial.h delete mode 100644 lib/EspSoftwareSerial-3.3.1/examples/swsertest/swsertest.ino delete mode 100644 lib/EspSoftwareSerial-3.3.1/keywords.txt delete mode 100644 lib/EspSoftwareSerial-3.3.1/library.json delete mode 100644 lib/EspSoftwareSerial-3.3.1/library.properties delete mode 100644 lib/EspSoftwareSerialNoIram-3.3.1/README.md delete mode 100644 lib/EspSoftwareSerialNoIram-3.3.1/SoftwareSerialNoIram.cpp delete mode 100644 lib/EspSoftwareSerialNoIram-3.3.1/SoftwareSerialNoIram.h delete mode 100644 lib/EspSoftwareSerialNoIram-3.3.1/examples/swsertest/swsertest.ino delete mode 100644 lib/EspSoftwareSerialNoIram-3.3.1/keywords.txt delete mode 100644 lib/EspSoftwareSerialNoIram-3.3.1/library.json delete mode 100644 lib/EspSoftwareSerialNoIram-3.3.1/library.properties create mode 100644 sonoff/xsns_15_mhz_hardserial.ino create mode 100644 sonoff/xsns_15_mhz_softserial_obsolete.ino diff --git a/lib/EspSoftwareSerial-3.3.1/README.md b/lib/EspSoftwareSerial-3.3.1/README.md deleted file mode 100644 index 43c4ef4a6..000000000 --- a/lib/EspSoftwareSerial-3.3.1/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# EspSoftwareSerial - -Implementation of the Arduino software serial library for the ESP8266 - -Same functionality as the corresponding AVR library but several instances can be active at the same time. -Speed up to 115200 baud is supported. The constructor also has an optional input buffer size. - -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 in high baud rates. - - diff --git a/lib/EspSoftwareSerial-3.3.1/SoftwareSerial.cpp b/lib/EspSoftwareSerial-3.3.1/SoftwareSerial.cpp deleted file mode 100644 index c498b7505..000000000 --- a/lib/EspSoftwareSerial-3.3.1/SoftwareSerial.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - -SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266. -Copyright (c) 2015-2016 Peter Lerup. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library 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 -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -#include - -// The Arduino standard GPIO routines are not enough, -// must use some from the Espressif SDK as well -extern "C" { -#include "gpio.h" -} - -#include - -#define MAX_PIN 15 - -// As the Arduino attachInterrupt has no parameter, lists of objects -// and callbacks corresponding to each possible GPIO pins have to be defined -SoftwareSerial *ObjList[MAX_PIN+1]; - -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(); }; - -static void (*ISRList[MAX_PIN+1])() = { - 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 -}; - -SoftwareSerial::SoftwareSerial(int receivePin, int transmitPin, bool inverse_logic, unsigned int buffSize) { - m_rxValid = m_txValid = m_txEnableValid = false; - m_buffer = NULL; - m_invert = inverse_logic; - m_overflow = false; - m_rxEnabled = false; - if (isValidGPIOpin(receivePin)) { - m_rxPin = receivePin; - m_buffSize = buffSize; - m_buffer = (uint8_t*)malloc(m_buffSize); - if (m_buffer != NULL) { - m_rxValid = true; - m_inPos = m_outPos = 0; - pinMode(m_rxPin, INPUT); - ObjList[m_rxPin] = this; - enableRx(true); - } - } - if (isValidGPIOpin(transmitPin) || transmitPin == 16) { - m_txValid = true; - m_txPin = transmitPin; - pinMode(m_txPin, OUTPUT); - digitalWrite(m_txPin, !m_invert); - } - // Default speed - begin(9600); -} - -SoftwareSerial::~SoftwareSerial() { - enableRx(false); - if (m_rxValid) - ObjList[m_rxPin] = NULL; - if (m_buffer) - free(m_buffer); -} - -bool SoftwareSerial::isValidGPIOpin(int pin) { - return (pin >= 0 && pin <= 5) || (pin >= 12 && pin <= MAX_PIN); -} - -void SoftwareSerial::begin(long speed) { - // Use getCycleCount() loop to get as exact timing as possible - m_bitTime = ESP.getCpuFreqMHz()*1000000/speed; - m_highSpeed = speed > 9600; - - if (!m_rxEnabled) - enableRx(true); -} - -long SoftwareSerial::baudRate() { - return ESP.getCpuFreqMHz()*1000000/m_bitTime; -} - -void SoftwareSerial::setTransmitEnablePin(int transmitEnablePin) { - if (isValidGPIOpin(transmitEnablePin)) { - m_txEnableValid = true; - m_txEnablePin = transmitEnablePin; - pinMode(m_txEnablePin, OUTPUT); - digitalWrite(m_txEnablePin, LOW); - } else { - m_txEnableValid = false; - } -} - -void SoftwareSerial::enableRx(bool on) { - if (m_rxValid) { - if (on) - attachInterrupt(m_rxPin, ISRList[m_rxPin], m_invert ? RISING : FALLING); - else - detachInterrupt(m_rxPin); - m_rxEnabled = on; - } -} - -int SoftwareSerial::read() { - if (!m_rxValid || (m_inPos == m_outPos)) return -1; - uint8_t ch = m_buffer[m_outPos]; - m_outPos = (m_outPos+1) % m_buffSize; - return ch; -} - -int SoftwareSerial::available() { - if (!m_rxValid) return 0; - int avail = m_inPos - m_outPos; - if (avail < 0) avail += m_buffSize; - return avail; -} - -#define WAIT { while (ESP.getCycleCount()-start < wait) if (!m_highSpeed) optimistic_yield(1); wait += m_bitTime; } - -size_t SoftwareSerial::write(uint8_t b) { - if (!m_txValid) return 0; - - if (m_invert) b = ~b; - if (m_highSpeed) - // Disable interrupts in order to get a clean transmit - cli(); - if (m_txEnableValid) digitalWrite(m_txEnablePin, HIGH); - unsigned long wait = m_bitTime; - digitalWrite(m_txPin, HIGH); - unsigned long start = ESP.getCycleCount(); - // Start bit; - digitalWrite(m_txPin, LOW); - WAIT; - for (int i = 0; i < 8; i++) { - digitalWrite(m_txPin, (b & 1) ? HIGH : LOW); - WAIT; - b >>= 1; - } - // Stop bit - digitalWrite(m_txPin, HIGH); - WAIT; - if (m_txEnableValid) digitalWrite(m_txEnablePin, LOW); - if (m_highSpeed) - sei(); - return 1; -} - -void SoftwareSerial::flush() { - m_inPos = m_outPos = 0; -} - -bool SoftwareSerial::overflow() { - bool res = m_overflow; - m_overflow = false; - return res; -} - -int SoftwareSerial::peek() { - if (!m_rxValid || (m_inPos == m_outPos)) return -1; - return m_buffer[m_outPos]; -} - -void ICACHE_RAM_ATTR SoftwareSerial::rxRead() { - // Advance the starting point for the samples but compensate for the - // initial delay which occurs before the interrupt is delivered - unsigned long wait = m_bitTime + m_bitTime/3 - 500; - unsigned long start = ESP.getCycleCount(); - uint8_t rec = 0; - for (int i = 0; i < 8; i++) { - WAIT; - rec >>= 1; - if (digitalRead(m_rxPin)) - rec |= 0x80; - } - if (m_invert) rec = ~rec; - // Stop bit - WAIT; - // Store the received value in the buffer unless we have an overflow - int next = (m_inPos+1) % m_buffSize; - if (next != m_outPos) { - m_buffer[m_inPos] = rec; - m_inPos = next; - } else { - m_overflow = true; - } - // 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_rxPin); -} diff --git a/lib/EspSoftwareSerial-3.3.1/SoftwareSerial.h b/lib/EspSoftwareSerial-3.3.1/SoftwareSerial.h deleted file mode 100644 index 75905ff39..000000000 --- a/lib/EspSoftwareSerial-3.3.1/SoftwareSerial.h +++ /dev/null @@ -1,88 +0,0 @@ -/* -SoftwareSerial.h - -SoftwareSerial.cpp - Implementation of the Arduino software serial for ESP8266. -Copyright (c) 2015-2016 Peter Lerup. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library 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 -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -#ifndef SoftwareSerial_h -#define SoftwareSerial_h - -#include -#include - - -// This class is compatible with the corresponding AVR one, -// the constructor however has an optional rx buffer size. -// Speed up to 115200 can be used. - - -class SoftwareSerial : public Stream -{ -public: - SoftwareSerial(int receivePin, int transmitPin, bool inverse_logic = false, unsigned int buffSize = 64); - ~SoftwareSerial(); - - void begin(long speed); - long baudRate(); - void setTransmitEnablePin(int transmitEnablePin); - - bool overflow(); - int peek(); - - virtual size_t write(uint8_t byte); - virtual int read(); - virtual int available(); - virtual void flush(); - operator bool() {return m_rxValid || m_txValid;} - - // Disable or enable interrupts on the rx pin - void enableRx(bool on); - - void rxRead(); - - // AVR compatibility methods - bool listen() { enableRx(true); return true; } - void end() { stopListening(); } - bool isListening() { return m_rxEnabled; } - bool stopListening() { enableRx(false); return true; } - - using Print::write; - -private: - bool isValidGPIOpin(int pin); - - // Member variables - int m_rxPin, m_txPin, m_txEnablePin; - bool m_rxValid, m_rxEnabled; - bool m_txValid, m_txEnableValid; - bool m_invert; - bool m_overflow; - unsigned long m_bitTime; - bool m_highSpeed; - unsigned int m_inPos, m_outPos; - int m_buffSize; - uint8_t *m_buffer; - -}; - -// If only one tx or rx wanted then use this as parameter for the unused pin -#define SW_SERIAL_UNUSED_PIN -1 - - -#endif diff --git a/lib/EspSoftwareSerial-3.3.1/examples/swsertest/swsertest.ino b/lib/EspSoftwareSerial-3.3.1/examples/swsertest/swsertest.ino deleted file mode 100644 index b3ae69711..000000000 --- a/lib/EspSoftwareSerial-3.3.1/examples/swsertest/swsertest.ino +++ /dev/null @@ -1,27 +0,0 @@ - -#include - -SoftwareSerial swSer(14, 12, false, 256); - -void setup() { - Serial.begin(115200); - swSer.begin(115200); - - Serial.println("\nSoftware 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/EspSoftwareSerial-3.3.1/keywords.txt b/lib/EspSoftwareSerial-3.3.1/keywords.txt deleted file mode 100644 index 5faea2a96..000000000 --- a/lib/EspSoftwareSerial-3.3.1/keywords.txt +++ /dev/null @@ -1,31 +0,0 @@ -####################################### -# Syntax Coloring Map for SoftwareSerial -# (esp8266) -####################################### - -####################################### -# Datatypes (KEYWORD1) -####################################### - -SoftwareSerial KEYWORD1 - -####################################### -# Methods and Functions (KEYWORD2) -####################################### - -begin KEYWORD2 -read KEYWORD2 -write KEYWORD2 -available KEYWORD2 -flush KEYWORD2 -overflow KEYWORD2 -peek KEYWORD2 -listen KEYWORD2 -end KEYWORD2 -isListening KEYWORD2 -stopListening KEYWORD2 - -####################################### -# Constants (LITERAL1) -####################################### - diff --git a/lib/EspSoftwareSerial-3.3.1/library.json b/lib/EspSoftwareSerial-3.3.1/library.json deleted file mode 100644 index 46e18b6be..000000000 --- a/lib/EspSoftwareSerial-3.3.1/library.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "EspSoftwareSerial", - "version": "3.3.1", - "keywords": [ - "serial", "io", "softwareserial" - ], - "description": "Implementation of the Arduino software serial for ESP8266.", - "repository": - { - "type": "git", - "url": "https://github.com/plerup/espsoftwareserial" - }, - "frameworks": "arduino", - "platforms": "espressif8266" -} diff --git a/lib/EspSoftwareSerial-3.3.1/library.properties b/lib/EspSoftwareSerial-3.3.1/library.properties deleted file mode 100644 index 9e53e159c..000000000 --- a/lib/EspSoftwareSerial-3.3.1/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=SoftwareSerial -version=1.0 -author=Peter Lerup -maintainer=Peter Lerup -sentence=Implementation of the Arduino software serial for ESP8266. -paragraph= -category=Signal Input/Output -url= -architectures=esp8266 diff --git a/lib/EspSoftwareSerialNoIram-3.3.1/README.md b/lib/EspSoftwareSerialNoIram-3.3.1/README.md deleted file mode 100644 index 43c4ef4a6..000000000 --- a/lib/EspSoftwareSerialNoIram-3.3.1/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# EspSoftwareSerial - -Implementation of the Arduino software serial library for the ESP8266 - -Same functionality as the corresponding AVR library but several instances can be active at the same time. -Speed up to 115200 baud is supported. The constructor also has an optional input buffer size. - -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 in high baud rates. - - diff --git a/lib/EspSoftwareSerialNoIram-3.3.1/SoftwareSerialNoIram.cpp b/lib/EspSoftwareSerialNoIram-3.3.1/SoftwareSerialNoIram.cpp deleted file mode 100644 index 7bcecc713..000000000 --- a/lib/EspSoftwareSerialNoIram-3.3.1/SoftwareSerialNoIram.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/* - -SoftwareSerialNoIram.cpp - Implementation of the Arduino software serial for ESP8266. -Copyright (c) 2015-2016 Peter Lerup. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library 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 -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -#define TASMOTA_NO_ICACHE_RAM // To solve compile errors due to lack off iram - -#include - -// The Arduino standard GPIO routines are not enough, -// must use some from the Espressif SDK as well -extern "C" { -#include "gpio.h" -} - -#include - -#define MAX_PIN 15 - -// As the Arduino attachInterrupt has no parameter, lists of objects -// and callbacks corresponding to each possible GPIO pins have to be defined -SoftwareSerialNoIram *ObjList[MAX_PIN+1]; - -#ifdef TASMOTA_NO_ICACHE_RAM -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(); }; -#else -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(); }; -#endif - -static void (*ISRList[MAX_PIN+1])() = { - 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 -}; - -SoftwareSerialNoIram::SoftwareSerialNoIram(int receivePin, int transmitPin, bool inverse_logic, unsigned int buffSize) { - m_rxValid = m_txValid = m_txEnableValid = false; - m_buffer = NULL; - m_invert = inverse_logic; - m_overflow = false; - m_rxEnabled = false; - if (isValidGPIOpin(receivePin)) { - m_rxPin = receivePin; - m_buffSize = buffSize; - m_buffer = (uint8_t*)malloc(m_buffSize); - if (m_buffer != NULL) { - m_rxValid = true; - m_inPos = m_outPos = 0; - pinMode(m_rxPin, INPUT); - ObjList[m_rxPin] = this; - enableRx(true); - } - } - if (isValidGPIOpin(transmitPin) || transmitPin == 16) { - m_txValid = true; - m_txPin = transmitPin; - pinMode(m_txPin, OUTPUT); - digitalWrite(m_txPin, !m_invert); - } - // Default speed - begin(9600); -} - -SoftwareSerialNoIram::~SoftwareSerialNoIram() { - enableRx(false); - if (m_rxValid) - ObjList[m_rxPin] = NULL; - if (m_buffer) - free(m_buffer); -} - -bool SoftwareSerialNoIram::isValidGPIOpin(int pin) { - return (pin >= 0 && pin <= 5) || (pin >= 12 && pin <= MAX_PIN); -} - -void SoftwareSerialNoIram::begin(long speed) { - // Use getCycleCount() loop to get as exact timing as possible - m_bitTime = ESP.getCpuFreqMHz()*1000000/speed; - m_highSpeed = speed > 9600; - - if (!m_rxEnabled) - enableRx(true); -} - -long SoftwareSerialNoIram::baudRate() { - return ESP.getCpuFreqMHz()*1000000/m_bitTime; -} - -void SoftwareSerialNoIram::setTransmitEnablePin(int transmitEnablePin) { - if (isValidGPIOpin(transmitEnablePin)) { - m_txEnableValid = true; - m_txEnablePin = transmitEnablePin; - pinMode(m_txEnablePin, OUTPUT); - digitalWrite(m_txEnablePin, LOW); - } else { - m_txEnableValid = false; - } -} - -void SoftwareSerialNoIram::enableRx(bool on) { - if (m_rxValid) { - if (on) - attachInterrupt(m_rxPin, ISRList[m_rxPin], m_invert ? RISING : FALLING); - else - detachInterrupt(m_rxPin); - m_rxEnabled = on; - } -} - -int SoftwareSerialNoIram::read() { - if (!m_rxValid || (m_inPos == m_outPos)) return -1; - uint8_t ch = m_buffer[m_outPos]; - m_outPos = (m_outPos+1) % m_buffSize; - return ch; -} - -int SoftwareSerialNoIram::available() { - if (!m_rxValid) return 0; - int avail = m_inPos - m_outPos; - if (avail < 0) avail += m_buffSize; - return avail; -} - -#define WAIT { while (ESP.getCycleCount()-start < wait) if (!m_highSpeed) optimistic_yield(1); wait += m_bitTime; } - -size_t SoftwareSerialNoIram::write(uint8_t b) { - if (!m_txValid) return 0; - - if (m_invert) b = ~b; - if (m_highSpeed) - // Disable interrupts in order to get a clean transmit - cli(); - if (m_txEnableValid) digitalWrite(m_txEnablePin, HIGH); - unsigned long wait = m_bitTime; - digitalWrite(m_txPin, HIGH); - unsigned long start = ESP.getCycleCount(); - // Start bit; - digitalWrite(m_txPin, LOW); - WAIT; - for (int i = 0; i < 8; i++) { - digitalWrite(m_txPin, (b & 1) ? HIGH : LOW); - WAIT; - b >>= 1; - } - // Stop bit - digitalWrite(m_txPin, HIGH); - WAIT; - if (m_txEnableValid) digitalWrite(m_txEnablePin, LOW); - if (m_highSpeed) - sei(); - return 1; -} - -void SoftwareSerialNoIram::flush() { - m_inPos = m_outPos = 0; -} - -bool SoftwareSerialNoIram::overflow() { - bool res = m_overflow; - m_overflow = false; - return res; -} - -int SoftwareSerialNoIram::peek() { - if (!m_rxValid || (m_inPos == m_outPos)) return -1; - return m_buffer[m_outPos]; -} - -#ifdef TASMOTA_NO_ICACHE_RAM -void SoftwareSerialNoIram::rxRead() { -#else -void ICACHE_RAM_ATTR SoftwareSerialNoIram::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_bitTime + m_bitTime/3 - 500; - unsigned long start = ESP.getCycleCount(); - uint8_t rec = 0; - for (int i = 0; i < 8; i++) { - WAIT; - rec >>= 1; - if (digitalRead(m_rxPin)) - rec |= 0x80; - } - if (m_invert) rec = ~rec; - // Stop bit - WAIT; - // Store the received value in the buffer unless we have an overflow - int next = (m_inPos+1) % m_buffSize; - if (next != m_outPos) { - m_buffer[m_inPos] = rec; - m_inPos = next; - } else { - m_overflow = true; - } - // 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_rxPin); -} diff --git a/lib/EspSoftwareSerialNoIram-3.3.1/SoftwareSerialNoIram.h b/lib/EspSoftwareSerialNoIram-3.3.1/SoftwareSerialNoIram.h deleted file mode 100644 index 4cec3b3b9..000000000 --- a/lib/EspSoftwareSerialNoIram-3.3.1/SoftwareSerialNoIram.h +++ /dev/null @@ -1,88 +0,0 @@ -/* -SoftwareSerialNoIram.h - -SoftwareSerialNoIram.cpp - Implementation of the Arduino software serial for ESP8266 without iram usage. -Copyright (c) 2015-2016 Peter Lerup. All rights reserved. - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -This library 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 -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -#ifndef SoftwareSerialNoIram_h -#define SoftwareSerialNoIram_h - -#include -#include - - -// This class is compatible with the corresponding AVR one, -// the constructor however has an optional rx buffer size. -// Speed up to 115200 can be used. - - -class SoftwareSerialNoIram : public Stream -{ -public: - SoftwareSerialNoIram(int receivePin, int transmitPin, bool inverse_logic = false, unsigned int buffSize = 64); - ~SoftwareSerialNoIram(); - - void begin(long speed); - long baudRate(); - void setTransmitEnablePin(int transmitEnablePin); - - bool overflow(); - int peek(); - - virtual size_t write(uint8_t byte); - virtual int read(); - virtual int available(); - virtual void flush(); - operator bool() {return m_rxValid || m_txValid;} - - // Disable or enable interrupts on the rx pin - void enableRx(bool on); - - void rxRead(); - - // AVR compatibility methods - bool listen() { enableRx(true); return true; } - void end() { stopListening(); } - bool isListening() { return m_rxEnabled; } - bool stopListening() { enableRx(false); return true; } - - using Print::write; - -private: - bool isValidGPIOpin(int pin); - - // Member variables - int m_rxPin, m_txPin, m_txEnablePin; - bool m_rxValid, m_rxEnabled; - bool m_txValid, m_txEnableValid; - bool m_invert; - bool m_overflow; - unsigned long m_bitTime; - bool m_highSpeed; - unsigned int m_inPos, m_outPos; - int m_buffSize; - uint8_t *m_buffer; - -}; - -// If only one tx or rx wanted then use this as parameter for the unused pin -#define SW_SERIAL_UNUSED_PIN -1 - - -#endif diff --git a/lib/EspSoftwareSerialNoIram-3.3.1/examples/swsertest/swsertest.ino b/lib/EspSoftwareSerialNoIram-3.3.1/examples/swsertest/swsertest.ino deleted file mode 100644 index b3ae69711..000000000 --- a/lib/EspSoftwareSerialNoIram-3.3.1/examples/swsertest/swsertest.ino +++ /dev/null @@ -1,27 +0,0 @@ - -#include - -SoftwareSerial swSer(14, 12, false, 256); - -void setup() { - Serial.begin(115200); - swSer.begin(115200); - - Serial.println("\nSoftware 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/EspSoftwareSerialNoIram-3.3.1/keywords.txt b/lib/EspSoftwareSerialNoIram-3.3.1/keywords.txt deleted file mode 100644 index 9f595c0e2..000000000 --- a/lib/EspSoftwareSerialNoIram-3.3.1/keywords.txt +++ /dev/null @@ -1,31 +0,0 @@ -####################################### -# Syntax Coloring Map for SoftwareSerialNoIram -# (esp8266) -####################################### - -####################################### -# Datatypes (KEYWORD1) -####################################### - -SoftwareSerialNoIram KEYWORD1 - -####################################### -# Methods and Functions (KEYWORD2) -####################################### - -begin KEYWORD2 -read KEYWORD2 -write KEYWORD2 -available KEYWORD2 -flush KEYWORD2 -overflow KEYWORD2 -peek KEYWORD2 -listen KEYWORD2 -end KEYWORD2 -isListening KEYWORD2 -stopListening KEYWORD2 - -####################################### -# Constants (LITERAL1) -####################################### - diff --git a/lib/EspSoftwareSerialNoIram-3.3.1/library.json b/lib/EspSoftwareSerialNoIram-3.3.1/library.json deleted file mode 100644 index 861d394a0..000000000 --- a/lib/EspSoftwareSerialNoIram-3.3.1/library.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "EspSoftwareSerialNoIram", - "version": "3.3.1", - "keywords": [ - "serial", "io", "softwareserialnoiram" - ], - "description": "Implementation of the Arduino software serial for ESP8266 without iram usage.", - "repository": - { - "type": "git", - "url": "https://github.com/plerup/espsoftwareserial" - }, - "frameworks": "arduino", - "platforms": "espressif8266" -} diff --git a/lib/EspSoftwareSerialNoIram-3.3.1/library.properties b/lib/EspSoftwareSerialNoIram-3.3.1/library.properties deleted file mode 100644 index b934964c1..000000000 --- a/lib/EspSoftwareSerialNoIram-3.3.1/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=SoftwareSerialNoIram -version=1.0 -author=Peter Lerup -maintainer=Peter Lerup -sentence=Implementation of the Arduino software serial for ESP8266 without iram usage. -paragraph= -category=Signal Input/Output -url= -architectures=esp8266 diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index e41a17d3f..050f18188 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,7 +1,8 @@ /* 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 in user_config.h (#561, #1248) - * Add (experimental) support for sensor MH-Z19(B) using SoftwareSerial to be enabled with define USE_MHZ19 in user_config.h (#561, #1248) + * Add support for sensor MH-Z19(B) using serial interface to be enabled with define USE_MHZ19_HARD_SERIAL in user_config.h (#561, #1248) + * Add (experimental) support for sensor MH-Z19(B) using SoftwareSerial to be enabled with define USE_MHZ19_SOFT_SERIAL_OBSOLETE in user_config.h (#561, #1248) + * Add (experimental) support for sensor MH-Z19(B) using stripped SoftwareSerial to be enabled with define USE_MHZ19_SOFT_SERIAL in user_config.h (#561, #1248) * Add support for iTead SI7021 temperature and humidity sensor by consolidating DHT22 into AM2301 and using former DHT22 as SI7021 (#735) * Fix BME280 calculation (#1051) * Add support for BME680 using adafruit libraries (#1212) diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 6d04f7a55..c0ceae097 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -167,7 +167,7 @@ //#define USE_DS18x20 // Optional for more than one DS18x20 sensors with id sort, single scan and read retry (+1k3 code) //#define USE_DS18x20_LEGACY // Optional for more than one DS18x20 sensors with dynamic scan using library OneWire (+1k5 code) -#define USE_I2C // I2C using library wire (+10k code, 0k2 mem) - Disable by // +#define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram) #define USE_SHT // Add I2C emulating code for SHT1X sensor (+1k4 code) // #define USE_SHT3X // Add I2C code for SHT3x sensor based on Adafruit (+0k7 code) // #define USE_SHT3X_V2 // Add I2C code for SHT3x sensor based on EspEasy (+0k7 code) @@ -181,17 +181,17 @@ // #define USE_ADS1115_I2CDEV // Add I2C code for ADS1115 16 bit A/D converter using library i2cdevlib-Core and i2cdevlib-ADS1115 (+2k code) // #define USE_INA219 // Add I2C code for INA219 Low voltage and current sensor (+1k code) -#define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem) +#define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram) // #define USE_IR_HVAC // Support for HVAC system using IR (+2k code) - #define USE_IR_RECEIVE // Support for IR receiver (+5k5 code) + #define USE_IR_RECEIVE // Support for IR receiver (+5k5 code, 264 iram) -#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem) - Disable by // +#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // #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 // Add support for MH-Z19 CO2 sensor using hardware serial interface at 9600 bps (+1k1 code) -//#define USE_MHZ19_SOFT_SERIAL // Add support for MH-Z19 CO2 sensor using iram free software serial interface at 9600 bps (+2k3 code) -// #define USE_SERIAL_NO_ICACHE // Use no iram with SoftwareSerial (may loose characters) +//#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_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code) diff --git a/sonoff/xsns_15_mhz_hardserial.ino b/sonoff/xsns_15_mhz_hardserial.ino new file mode 100644 index 000000000..cde575f2d --- /dev/null +++ b/sonoff/xsns_15_mhz_hardserial.ino @@ -0,0 +1,277 @@ +/* + 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 index a91dd8c3b..b77faeb20 100644 --- a/sonoff/xsns_15_mhz_softserial.ino +++ b/sonoff/xsns_15_mhz_softserial.ino @@ -28,14 +28,6 @@ * Select filter usage on low stability readings \*********************************************************************************************/ -#ifdef USE_SERIAL_NO_ICACHE - #include - SoftwareSerialNoIram *SoftSerial; -#else - #include - SoftwareSerial *SoftSerial; -#endif // USE_SERIAL_NO_ICACHE - 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 @@ -65,18 +57,136 @@ enum Mhz19FilterOptions {MHZ19_FILTER_OFF, MHZ19_FILTER_OFF_ALLSAMPLES, MHZ19_FI 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}; +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; -byte mhz19_response[9]; +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) { @@ -98,28 +208,15 @@ bool Mhz19CheckAndApplyFilter(uint16_t ppm, uint8_t s) // 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 *= s; difference /= 64; } - switch (mhz19_filter) { - case MHZ19_FILTER_OFF: { - if (s != 0 && s != 64) { - return false; - } - break; + if (MHZ19_FILTER_OFF == mhz19_filter) { + if (s != 0 && s != 64) { + return false; } - // #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 + } else { + difference >>= (mhz19_filter -1); } mhz19_last_ppm = static_cast(mhz19_last_ppm + difference); return true; @@ -134,16 +231,16 @@ bool Mhz19Read(uint16_t &p, float &t) if (mhz19_type) { - SoftSerial->flush(); - if (SoftSerial->write(mhz19_cmnd_read_ppm, 9) != 9) { + 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 (SoftSerial->available() > 0) { - mhz19_response[counter++] = SoftSerial->read(); + if (Mhz19SerialAvailable() > 0) { + mhz19_response[counter++] = Mhz19SerialRead(); } else { delay(10); } @@ -194,9 +291,9 @@ bool Mhz19Read(uint16_t &p, float &t) 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 + Mhz19SerialWrite(mhz19_cmnd_abc_enable, 9); // Sent sensor ABC Enable } else { - SoftSerial->write(mhz19_cmnd_abc_disable, 9); // Sent sensor ABC Disable + Mhz19SerialWrite(mhz19_cmnd_abc_disable, 9); // Sent sensor ABC Disable } } } @@ -211,14 +308,9 @@ bool Mhz19Read(uint16_t &p, float &t) void Mhz19Init() { -#ifdef USE_SERIAL_NO_ICACHE - SoftSerial = new SoftwareSerialNoIram(pin[GPIO_MHZ_RXD], pin[GPIO_MHZ_TXD]); -#else - SoftSerial = new SoftwareSerial(pin[GPIO_MHZ_RXD], pin[GPIO_MHZ_TXD]); -#endif // USE_SERIAL_NO_ICACHE - - SoftSerial->begin(9600); - mhz19_type = 1; + if (Mhz19Serial(pin[GPIO_MHZ_RXD], pin[GPIO_MHZ_TXD])) { + mhz19_type = 1; + } } #ifdef USE_WEBSERVER diff --git a/sonoff/xsns_15_mhz_softserial_obsolete.ino b/sonoff/xsns_15_mhz_softserial_obsolete.ino new file mode 100644 index 000000000..4f468b17e --- /dev/null +++ b/sonoff/xsns_15_mhz_softserial_obsolete.ino @@ -0,0 +1,277 @@ +/* + 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