mirror of https://github.com/arendst/Tasmota.git
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)
This commit is contained in:
parent
4cf64af51e
commit
0747e0fc68
|
@ -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.
|
||||
|
||||
|
|
@ -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 <Arduino.h>
|
||||
|
||||
// The Arduino standard GPIO routines are not enough,
|
||||
// must use some from the Espressif SDK as well
|
||||
extern "C" {
|
||||
#include "gpio.h"
|
||||
}
|
||||
|
||||
#include <SoftwareSerial.h>
|
||||
|
||||
#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);
|
||||
}
|
|
@ -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 <inttypes.h>
|
||||
#include <Stream.h>
|
||||
|
||||
|
||||
// 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
|
|
@ -1,27 +0,0 @@
|
|||
|
||||
#include <SoftwareSerial.h>
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
#######################################
|
||||
|
|
@ -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"
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
name=SoftwareSerial
|
||||
version=1.0
|
||||
author=Peter Lerup
|
||||
maintainer=Peter Lerup <peter@lerup.com>
|
||||
sentence=Implementation of the Arduino software serial for ESP8266.
|
||||
paragraph=
|
||||
category=Signal Input/Output
|
||||
url=
|
||||
architectures=esp8266
|
|
@ -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.
|
||||
|
||||
|
|
@ -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 <Arduino.h>
|
||||
|
||||
// The Arduino standard GPIO routines are not enough,
|
||||
// must use some from the Espressif SDK as well
|
||||
extern "C" {
|
||||
#include "gpio.h"
|
||||
}
|
||||
|
||||
#include <SoftwareSerialNoIram.h>
|
||||
|
||||
#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);
|
||||
}
|
|
@ -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 <inttypes.h>
|
||||
#include <Stream.h>
|
||||
|
||||
|
||||
// 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
|
|
@ -1,27 +0,0 @@
|
|||
|
||||
#include <SoftwareSerial.h>
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
#######################################
|
||||
|
|
@ -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"
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
name=SoftwareSerialNoIram
|
||||
version=1.0
|
||||
author=Peter Lerup
|
||||
maintainer=Peter Lerup <peter@lerup.com>
|
||||
sentence=Implementation of the Arduino software serial for ESP8266 without iram usage.
|
||||
paragraph=
|
||||
category=Signal Input/Output
|
||||
url=
|
||||
architectures=esp8266
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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<uint16_t>(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} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
||||
#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
|
|
@ -28,14 +28,6 @@
|
|||
* Select filter usage on low stability readings
|
||||
\*********************************************************************************************/
|
||||
|
||||
#ifdef USE_SERIAL_NO_ICACHE
|
||||
#include <SoftwareSerialNoIram.h>
|
||||
SoftwareSerialNoIram *SoftSerial;
|
||||
#else
|
||||
#include <SoftwareSerial.h>
|
||||
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<uint16_t>(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
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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.h>
|
||||
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<uint16_t>(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} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
||||
#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
|
Loading…
Reference in New Issue