Merge branch 'arendst/development' into development

This commit is contained in:
reloxx13 2017-12-26 20:01:30 +01:00
commit d3f5f0161c
55 changed files with 1202 additions and 1418 deletions

View File

@ -1,7 +1,7 @@
## Sonoff-Tasmota ## Sonoff-Tasmota
Provide ESP8266 based Sonoff by [iTead Studio](https://www.itead.cc/) and ElectroDragon IoT Relay with Serial, Web and MQTT control allowing 'Over the Air' or OTA firmware updates using Arduino IDE. Provide ESP8266 based Sonoff by [iTead Studio](https://www.itead.cc/) and ElectroDragon IoT Relay with Serial, Web and MQTT control allowing 'Over the Air' or OTA firmware updates using Arduino IDE.
Current version is **5.10.0b** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for change information. Current version is **5.10.0c** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for change information.
### ATTENTION All versions ### ATTENTION All versions

View File

@ -0,0 +1,8 @@
# TasmotaSerial
Implementation of software serial library for the ESP8266 at 9600 baud
Allows for several instances to be active at the same time.
Please note that due to the fact that the ESP always have other activities ongoing, there will be some inexactness in interrupt
timings. This may lead to bit errors when having heavy data traffic.

View File

@ -0,0 +1,27 @@
#include <TasmotaSerial.h>
TasmotaSerial swSer(14, 12);
void setup() {
Serial.begin(115200);
swSer.begin();
Serial.println("\nTasmota serial test started");
for (char ch = ' '; ch <= 'z'; ch++) {
swSer.write(ch);
}
swSer.println("");
}
void loop() {
while (swSer.available() > 0) {
Serial.write(swSer.read());
}
while (Serial.available() > 0) {
swSer.write(Serial.read());
}
}

View File

@ -0,0 +1,26 @@
#######################################
# Syntax Coloring Map for TasmotaSerial
# (esp8266)
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
TasmotaSerial KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
read KEYWORD2
write KEYWORD2
available KEYWORD2
flush KEYWORD2
peek KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View File

@ -0,0 +1,15 @@
{
"name": "TasmotaSerial",
"version": "1.0.0",
"keywords": [
"serial", "io", "TasmotaSerial"
],
"description": "Implementation of software serial for ESP8266 at 9600 baud.",
"repository":
{
"type": "git",
"url": "https://github.com/arendst/Sonoff-Tasmota/lib/TasmotaSerial"
},
"frameworks": "arduino",
"platforms": "espressif8266"
}

View File

@ -0,0 +1,9 @@
name=TasmotaSerial
version=1.0
author=Theo Arends
maintainer=Theo Arends <theo@arends.com>
sentence=Implementation of software serial for ESP8266 at 9600 baud.
paragraph=
category=Signal Input/Output
url=
architectures=esp8266

View File

@ -0,0 +1,193 @@
/*
TasmotaSerial.cpp - Minimal implementation of software serial for Tasmota
Copyright (C) 2018 Theo Arends
This library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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 <TasmotaSerial.h>
// As the Arduino attachInterrupt has no parameter, lists of objects
// and callbacks corresponding to each possible GPIO pins have to be defined
TasmotaSerial *ObjList[16];
#ifdef TM_SERIAL_USE_IRAM
void ICACHE_RAM_ATTR sws_isr_0() { ObjList[0]->rxRead(); };
void ICACHE_RAM_ATTR sws_isr_1() { ObjList[1]->rxRead(); };
void ICACHE_RAM_ATTR sws_isr_2() { ObjList[2]->rxRead(); };
void ICACHE_RAM_ATTR sws_isr_3() { ObjList[3]->rxRead(); };
void ICACHE_RAM_ATTR sws_isr_4() { ObjList[4]->rxRead(); };
void ICACHE_RAM_ATTR sws_isr_5() { ObjList[5]->rxRead(); };
// Pin 6 to 11 can not be used
void ICACHE_RAM_ATTR sws_isr_12() { ObjList[12]->rxRead(); };
void ICACHE_RAM_ATTR sws_isr_13() { ObjList[13]->rxRead(); };
void ICACHE_RAM_ATTR sws_isr_14() { ObjList[14]->rxRead(); };
void ICACHE_RAM_ATTR sws_isr_15() { ObjList[15]->rxRead(); };
#else
void sws_isr_0() { ObjList[0]->rxRead(); };
void sws_isr_1() { ObjList[1]->rxRead(); };
void sws_isr_2() { ObjList[2]->rxRead(); };
void sws_isr_3() { ObjList[3]->rxRead(); };
void sws_isr_4() { ObjList[4]->rxRead(); };
void sws_isr_5() { ObjList[5]->rxRead(); };
// Pin 6 to 11 can not be used
void sws_isr_12() { ObjList[12]->rxRead(); };
void sws_isr_13() { ObjList[13]->rxRead(); };
void sws_isr_14() { ObjList[14]->rxRead(); };
void sws_isr_15() { ObjList[15]->rxRead(); };
#endif // TM_SERIAL_USE_IRAM
static void (*ISRList[16])() = {
sws_isr_0,
sws_isr_1,
sws_isr_2,
sws_isr_3,
sws_isr_4,
sws_isr_5,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
sws_isr_12,
sws_isr_13,
sws_isr_14,
sws_isr_15
};
TasmotaSerial::TasmotaSerial(int receive_pin, int transmit_pin)
{
m_valid = false;
if (!((isValidGPIOpin(receive_pin)) && (isValidGPIOpin(transmit_pin) || transmit_pin == 16))) {
return;
}
m_buffer = (uint8_t*)malloc(TM_SERIAL_BUFFER_SIZE);
if (m_buffer == NULL) {
return;
}
m_valid = true;
m_rx_pin = receive_pin;
m_tx_pin = transmit_pin;
m_in_pos = m_out_pos = 0;
// Use getCycleCount() loop to get as exact timing as possible
m_bit_time = ESP.getCpuFreqMHz() *1000000 /TM_SERIAL_BAUDRATE;
pinMode(m_rx_pin, INPUT);
ObjList[m_rx_pin] = this;
attachInterrupt(m_rx_pin, ISRList[m_rx_pin], FALLING);
pinMode(m_tx_pin, OUTPUT);
digitalWrite(m_tx_pin, HIGH);
}
bool TasmotaSerial::isValidGPIOpin(int pin)
{
return (pin >= 0 && pin <= 5) || (pin >= 12 && pin <= 15);
}
bool TasmotaSerial::begin() {
return m_valid;
}
int TasmotaSerial::read()
{
if (m_in_pos == m_out_pos) {
return -1;
}
uint8_t ch = m_buffer[m_out_pos];
m_out_pos = (m_out_pos +1) % TM_SERIAL_BUFFER_SIZE;
return ch;
}
int TasmotaSerial::available()
{
int avail = m_in_pos - m_out_pos;
if (avail < 0) {
avail += TM_SERIAL_BUFFER_SIZE;
}
return avail;
}
//#define TM_SERIAL_WAIT { while (ESP.getCycleCount()-start < wait) optimistic_yield(1); wait += m_bit_time; } // Watchdog timeouts
#define TM_SERIAL_WAIT { while (ESP.getCycleCount()-start < wait); wait += m_bit_time; }
size_t TasmotaSerial::txWrite(uint8_t b)
{
unsigned long wait = m_bit_time;
digitalWrite(m_tx_pin, HIGH);
unsigned long start = ESP.getCycleCount();
// Start bit;
digitalWrite(m_tx_pin, LOW);
TM_SERIAL_WAIT;
for (int i = 0; i < 8; i++) {
digitalWrite(m_tx_pin, (b & 1) ? HIGH : LOW);
TM_SERIAL_WAIT;
b >>= 1;
}
// Stop bit
digitalWrite(m_tx_pin, HIGH);
TM_SERIAL_WAIT;
return 1;
}
size_t TasmotaSerial::write(const uint8_t *buffer, size_t size)
{
size_t n = 0;
// Flush input buffer on every write
m_in_pos = m_out_pos = 0;
while(size--) {
n += txWrite(*buffer++);
}
return n;
}
#ifdef TM_SERIAL_USE_IRAM
void ICACHE_RAM_ATTR TasmotaSerial::rxRead()
{
#else
void TasmotaSerial::rxRead()
{
#endif
// Advance the starting point for the samples but compensate for the
// initial delay which occurs before the interrupt is delivered
unsigned long wait = m_bit_time + m_bit_time/3 - 500;
unsigned long start = ESP.getCycleCount();
uint8_t rec = 0;
for (int i = 0; i < 8; i++) {
TM_SERIAL_WAIT;
rec >>= 1;
if (digitalRead(m_rx_pin)) {
rec |= 0x80;
}
}
// Stop bit
TM_SERIAL_WAIT;
// Store the received value in the buffer unless we have an overflow
int next = (m_in_pos+1) % TM_SERIAL_BUFFER_SIZE;
if (next != m_out_pos) {
m_buffer[m_in_pos] = rec;
m_in_pos = next;
}
// Must clear this bit in the interrupt register,
// it gets set even when interrupts are disabled
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << m_rx_pin);
}

View File

@ -0,0 +1,56 @@
/*
TasmotaSerial.h - Minimal implementation of software serial for Tasmota
Copyright (C) 2018 Theo Arends
This library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TasmotaSerial_h
#define TasmotaSerial_h
/*********************************************************************************************\
* TasmotaSerial supports 9600 baud with fixed buffer size of 20 bytes using optional no iram
*
* Based on EspSoftwareSerial v3.3.1 by Peter Lerup (https://github.com/plerup/espsoftwareserial)
\*********************************************************************************************/
#define TM_SERIAL_BAUDRATE 9600
#define TM_SERIAL_BUFFER_SIZE 20
//#define TM_SERIAL_USE_IRAM // Enable to use iram (+368 bytes)
class TasmotaSerial {
public:
TasmotaSerial(int receive_pin, int transmit_pin);
bool begin();
size_t write(const uint8_t *buffer, size_t size = 1);
int read();
int available();
void rxRead();
private:
bool isValidGPIOpin(int pin);
size_t txWrite(uint8_t byte);
// Member variables
bool m_valid;
int m_rx_pin;
int m_tx_pin;
unsigned long m_bit_time;
unsigned int m_in_pos;
unsigned int m_out_pos;
uint8_t *m_buffer;
};
#endif // TasmotaSerial_h

View File

@ -1,14 +1,20 @@
/* 5.10.0b /* 5.10.0c
* Add optional support for PZEM004T energy sensor * Consolidate device serial (MH-Z19, SenseAir and Pzem004T) into TasmotaSerial library
* Change Sonoff Pow Enenrgy MQTT data message and consolidate Status 8 into Status 10 * Consolidate PWM device recognition
* Fix Wemo Emulation (#1357)
* Add support for Arilux LC06 (#1414)
*
* 5.10.0b
* Add support for PZEM004T energy sensor to be enabled with define USE_PZEM004T in user_config.h
* Change Sonoff Pow Energy MQTT data message and consolidate Status 8 into Status 10
* Change Wemo SetBinaryState to distinguish from GetBinaryState (#1357) * Change Wemo SetBinaryState to distinguish from GetBinaryState (#1357)
* Change output of HTTP command to valid JSON only (#1363) * Change output of HTTP command to valid JSON and Array only (#1363)
* Add support for MH-Z19(B) CO2 sensor to be enabled with define USE_MHZ19 in user_config.h (#561, #1248)
* Add support for SenseAir S8 CO2 sensor to be enabled with define USE_SENSEAIR in user_config.h
* Add support for Domoticz Air Quality sensor to be used by MH-Z19(B) and SenseAir sensors
* *
* 5.10.0a * 5.10.0a
* Add (experimental) support for sensor SHT3x * 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) * 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) * Fix BME280 calculation (#1051)
* Add support for BME680 using adafruit libraries (#1212) * Add support for BME680 using adafruit libraries (#1212)

View File

@ -1,7 +1,7 @@
/* /*
i18n.h - internationalization for Sonoff-Tasmota i18n.h - internationalization for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -121,6 +121,10 @@ const char HTTP_SNS_PRESSURE[] PROGMEM = "%s{s}%s " D_PRESSURE "{m}%s " D_UNIT_P
const char HTTP_SNS_SEAPRESSURE[] PROGMEM = "%s{s}%s " D_PRESSUREATSEALEVEL "{m}%s " D_UNIT_PRESSURE "{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr> const char HTTP_SNS_SEAPRESSURE[] PROGMEM = "%s{s}%s " D_PRESSUREATSEALEVEL "{m}%s " D_UNIT_PRESSURE "{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
const char HTTP_SNS_ANALOG[] PROGMEM = "%s{s}%s " D_ANALOG_INPUT "%d{m}%d{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr> const char HTTP_SNS_ANALOG[] PROGMEM = "%s{s}%s " D_ANALOG_INPUT "%d{m}%d{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
#if defined(USE_MHZ19) || defined(USE_SENSEAIR)
const char HTTP_SNS_CO2[] PROGMEM = "%s{s}%s " D_CO2 "{m}%d " D_UNIT_PPM "{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
#endif // USE_WEBSERVER
const char S_MAIN_MENU[] PROGMEM = D_MAIN_MENU; const char S_MAIN_MENU[] PROGMEM = D_MAIN_MENU;
const char S_CONFIGURATION[] PROGMEM = D_CONFIGURATION; const char S_CONFIGURATION[] PROGMEM = D_CONFIGURATION;
const char S_CONFIGURE_MODULE[] PROGMEM = D_CONFIGURE_MODULE; const char S_CONFIGURE_MODULE[] PROGMEM = D_CONFIGURE_MODULE;

View File

@ -1,7 +1,7 @@
/* /*
de-DE.h - localization for German - Germany for Sonoff-Tasmota de-DE.h - localization for German - Germany for Sonoff-Tasmota
Copyright (C) 2017 VinceMasuka Copyright (C) 2018 VinceMasuka
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -66,7 +66,7 @@
#define D_BUTTON "Knopf" #define D_BUTTON "Knopf"
#define D_BY "von" // Written by me #define D_BY "von" // Written by me
#define D_CELSIUS "Celsius" #define D_CELSIUS "Celsius"
#define D_CO2 "CO2" #define D_CO2 "Kohlenstoffdioxid"
#define D_CODE "code" // Button code #define D_CODE "code" // Button code
#define D_COLDLIGHT "kalt" #define D_COLDLIGHT "kalt"
#define D_COMMAND "Befehl" #define D_COMMAND "Befehl"
@ -118,6 +118,7 @@
#define D_INITIALIZED "initialisiert" #define D_INITIALIZED "initialisiert"
#define D_IP_ADDRESS "IP-Addresse" #define D_IP_ADDRESS "IP-Addresse"
#define D_LIGHT "Licht" #define D_LIGHT "Licht"
#define D_LIMIT "Grenzwert"
#define D_LOCAL_TIME "lokale Zeit" #define D_LOCAL_TIME "lokale Zeit"
#define D_LOW "niedrig" #define D_LOW "niedrig"
#define D_LWT "LWT" #define D_LWT "LWT"
@ -386,6 +387,7 @@
#define D_DOMOTICZ_COUNT "Count" #define D_DOMOTICZ_COUNT "Count"
#define D_DOMOTICZ_VOLTAGE "Voltage" #define D_DOMOTICZ_VOLTAGE "Voltage"
#define D_DOMOTICZ_CURRENT "Current" #define D_DOMOTICZ_CURRENT "Current"
#define D_DOMOTICZ_AIRQUALITY "AirQuality"
#define D_DOMOTICZ_UPDATE_TIMER "Update timer" #define D_DOMOTICZ_UPDATE_TIMER "Update timer"
// xdrv_irremote.ino // xdrv_irremote.ino
@ -479,6 +481,8 @@
#define D_SENSOR_MHZ_TX "MHZ Tx" #define D_SENSOR_MHZ_TX "MHZ Tx"
#define D_SENSOR_PZEM_RX "PZEM Rx" #define D_SENSOR_PZEM_RX "PZEM Rx"
#define D_SENSOR_PZEM_TX "PZEM Tx" #define D_SENSOR_PZEM_TX "PZEM Tx"
#define D_SENSOR_SAIR_RX "SAir Rx"
#define D_SENSOR_SAIR_TX "SAir Tx"
#define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_CS "SPI CS"
#define D_SENSOR_SPI_DC "SPI DC" #define D_SENSOR_SPI_DC "SPI DC"
#define D_SENSOR_BACKLIGHT "BLight" #define D_SENSOR_BACKLIGHT "BLight"

View File

@ -1,7 +1,7 @@
/* /*
en-GB.h - localization for English - United Kingdom for Sonoff-Tasmota en-GB.h - localization for English - United Kingdom for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -66,7 +66,7 @@
#define D_BUTTON "Button" #define D_BUTTON "Button"
#define D_BY "by" // Written by me #define D_BY "by" // Written by me
#define D_CELSIUS "Celsius" #define D_CELSIUS "Celsius"
#define D_CO2 "CO2" #define D_CO2 "Carbon dioxide"
#define D_CODE "code" // Button code #define D_CODE "code" // Button code
#define D_COLDLIGHT "Cold" #define D_COLDLIGHT "Cold"
#define D_COMMAND "Command" #define D_COMMAND "Command"
@ -118,6 +118,7 @@
#define D_INITIALIZED "Initialized" #define D_INITIALIZED "Initialized"
#define D_IP_ADDRESS "IP Address" #define D_IP_ADDRESS "IP Address"
#define D_LIGHT "Light" #define D_LIGHT "Light"
#define D_LIMIT "Limit"
#define D_LOCAL_TIME "Local" #define D_LOCAL_TIME "Local"
#define D_LOW "Low" #define D_LOW "Low"
#define D_LWT "LWT" #define D_LWT "LWT"
@ -386,6 +387,7 @@
#define D_DOMOTICZ_COUNT "Count" #define D_DOMOTICZ_COUNT "Count"
#define D_DOMOTICZ_VOLTAGE "Voltage" #define D_DOMOTICZ_VOLTAGE "Voltage"
#define D_DOMOTICZ_CURRENT "Current" #define D_DOMOTICZ_CURRENT "Current"
#define D_DOMOTICZ_AIRQUALITY "AirQuality"
#define D_DOMOTICZ_UPDATE_TIMER "Update timer" #define D_DOMOTICZ_UPDATE_TIMER "Update timer"
// xdrv_irremote.ino // xdrv_irremote.ino
@ -479,6 +481,8 @@
#define D_SENSOR_MHZ_TX "MHZ Tx" #define D_SENSOR_MHZ_TX "MHZ Tx"
#define D_SENSOR_PZEM_RX "PZEM Rx" #define D_SENSOR_PZEM_RX "PZEM Rx"
#define D_SENSOR_PZEM_TX "PZEM Tx" #define D_SENSOR_PZEM_TX "PZEM Tx"
#define D_SENSOR_SAIR_RX "SAir Rx"
#define D_SENSOR_SAIR_TX "SAir Tx"
#define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_CS "SPI CS"
#define D_SENSOR_SPI_DC "SPI DC" #define D_SENSOR_SPI_DC "SPI DC"
#define D_SENSOR_BACKLIGHT "BLight" #define D_SENSOR_BACKLIGHT "BLight"

View File

@ -1,7 +1,7 @@
/* /*
nl-NL.h - localization for Dutch - Nederland for Sonoff-Tasmota nl-NL.h - localization for Dutch - Nederland for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -66,7 +66,7 @@
#define D_BUTTON "DrukKnop" #define D_BUTTON "DrukKnop"
#define D_BY "door" // Written by me #define D_BY "door" // Written by me
#define D_CELSIUS "Celsius" #define D_CELSIUS "Celsius"
#define D_CO2 "CO2" #define D_CO2 "Koolstofdioxide"
#define D_CODE "code" // Button code #define D_CODE "code" // Button code
#define D_COLDLIGHT "Koud" #define D_COLDLIGHT "Koud"
#define D_COMMAND "Opdracht" #define D_COMMAND "Opdracht"
@ -118,6 +118,7 @@
#define D_INITIALIZED "Geinitialiseerd" #define D_INITIALIZED "Geinitialiseerd"
#define D_IP_ADDRESS "IP Adres" #define D_IP_ADDRESS "IP Adres"
#define D_LIGHT "Ligt" #define D_LIGHT "Ligt"
#define D_LIMIT "Grenswaarde"
#define D_LOCAL_TIME "Plaatselijk" #define D_LOCAL_TIME "Plaatselijk"
#define D_LOW "Laag" #define D_LOW "Laag"
#define D_LWT "LWT" #define D_LWT "LWT"
@ -386,6 +387,7 @@
#define D_DOMOTICZ_COUNT "Count" #define D_DOMOTICZ_COUNT "Count"
#define D_DOMOTICZ_VOLTAGE "Spanning" #define D_DOMOTICZ_VOLTAGE "Spanning"
#define D_DOMOTICZ_CURRENT "Stroom" #define D_DOMOTICZ_CURRENT "Stroom"
#define D_DOMOTICZ_AIRQUALITY "AirQuality"
#define D_DOMOTICZ_UPDATE_TIMER "Bijwerk timer" #define D_DOMOTICZ_UPDATE_TIMER "Bijwerk timer"
// xdrv_irremote.ino // xdrv_irremote.ino
@ -474,11 +476,13 @@
#define D_SENSOR_LED "Led" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i"
#define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_PWM "PWM" // Suffix "1"
#define D_SENSOR_COUNTER "Teller" // Suffix "1" #define D_SENSOR_COUNTER "Teller" // Suffix "1"
#define D_SENSOR_IRRECV "IRrecv"
#define D_SENSOR_MHZ_RX "MHZ Rx" #define D_SENSOR_MHZ_RX "MHZ Rx"
#define D_SENSOR_MHZ_TX "MHZ Tx" #define D_SENSOR_MHZ_TX "MHZ Tx"
#define D_SENSOR_PZEM_RX "PZEM Rx" #define D_SENSOR_PZEM_RX "PZEM Rx"
#define D_SENSOR_PZEM_TX "PZEM Tx" #define D_SENSOR_PZEM_TX "PZEM Tx"
#define D_SENSOR_IRRECV "IRrecv" #define D_SENSOR_SAIR_RX "SAir Rx"
#define D_SENSOR_SAIR_TX "SAir Tx"
#define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_CS "SPI CS"
#define D_SENSOR_SPI_DC "SPI DC" #define D_SENSOR_SPI_DC "SPI DC"
#define D_SENSOR_BACKLIGHT "BLight" #define D_SENSOR_BACKLIGHT "BLight"

View File

@ -1,7 +1,7 @@
/* /*
pl-PL.h - localization for Polish without fonetick - Poland for Sonoff-Tasmota pl-PL.h - localization for Polish without fonetick - Poland for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends (translated by roblad - Robert L.) Copyright (C) 2018 Theo Arends (translated by roblad - Robert L.)
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -66,7 +66,7 @@
#define D_BUTTON "Przycisk" #define D_BUTTON "Przycisk"
#define D_BY "by" // Written by me #define D_BY "by" // Written by me
#define D_CELSIUS "Celsiusza" #define D_CELSIUS "Celsiusza"
#define D_CO2 "CO2" #define D_CO2 "Dwutlenku węgla"
#define D_CODE "kod" // Button code #define D_CODE "kod" // Button code
#define D_COLDLIGHT "Zimny" #define D_COLDLIGHT "Zimny"
#define D_COMMAND "Komenda" #define D_COMMAND "Komenda"
@ -118,6 +118,7 @@
#define D_INITIALIZED "Zainicjowany" #define D_INITIALIZED "Zainicjowany"
#define D_IP_ADDRESS "Adres IP" #define D_IP_ADDRESS "Adres IP"
#define D_LIGHT "Swiatlo" #define D_LIGHT "Swiatlo"
#define D_LIMIT "Wartość graniczna"
#define D_LOCAL_TIME "Lokalny" #define D_LOCAL_TIME "Lokalny"
#define D_LOW "Niski" #define D_LOW "Niski"
#define D_LWT "LWT" #define D_LWT "LWT"
@ -386,6 +387,7 @@
#define D_DOMOTICZ_COUNT "Licznik" #define D_DOMOTICZ_COUNT "Licznik"
#define D_DOMOTICZ_VOLTAGE "Napiecie" #define D_DOMOTICZ_VOLTAGE "Napiecie"
#define D_DOMOTICZ_CURRENT "Prad" #define D_DOMOTICZ_CURRENT "Prad"
#define D_DOMOTICZ_AIRQUALITY "AirQuality"
#define D_DOMOTICZ_UPDATE_TIMER "Zaktualizuj czasomierz" #define D_DOMOTICZ_UPDATE_TIMER "Zaktualizuj czasomierz"
// xdrv_irremote.ino // xdrv_irremote.ino
@ -479,6 +481,8 @@
#define D_SENSOR_MHZ_TX "MHZ Tx" #define D_SENSOR_MHZ_TX "MHZ Tx"
#define D_SENSOR_PZEM_RX "PZEM Rx" #define D_SENSOR_PZEM_RX "PZEM Rx"
#define D_SENSOR_PZEM_TX "PZEM Tx" #define D_SENSOR_PZEM_TX "PZEM Tx"
#define D_SENSOR_SAIR_RX "SAir Rx"
#define D_SENSOR_SAIR_TX "SAir Tx"
#define D_SENSOR_SPI_CS "SPI CS" #define D_SENSOR_SPI_CS "SPI CS"
#define D_SENSOR_SPI_DC "SPI DC" #define D_SENSOR_SPI_DC "SPI DC"
#define D_SENSOR_BACKLIGHT "BLight" #define D_SENSOR_BACKLIGHT "BLight"

View File

@ -1,7 +1,7 @@
/* /*
settings.h - setting variables for Sonoff-Tasmota settings.h - setting variables for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -202,7 +202,7 @@ struct SYSCFG {
uint8_t sleep; // 453 uint8_t sleep; // 453
uint16_t domoticz_switch_idx[MAX_DOMOTICZ_IDX]; // 454 uint16_t domoticz_switch_idx[MAX_DOMOTICZ_IDX]; // 454
uint16_t domoticz_sensor_idx[12]; // 45C uint16_t domoticz_sensor_idx[MAX_DOMOTICZ_SNS_IDX]; // 45C
uint8_t module; // 474 uint8_t module; // 474
uint8_t ws_color[4][3]; // 475 uint8_t ws_color[4][3]; // 475

View File

@ -1,7 +1,7 @@
/* /*
settings.ino - user settings for Sonoff-Tasmota settings.ino - user settings for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -182,7 +182,7 @@ void SettingsSaveAll()
} else { } else {
Settings.power = 0; Settings.power = 0;
} }
XsnsCall(FUNC_XSNS_SAVE_STATE); XsnsCall(FUNC_SAVE_BEFORE_RESTART);
SettingsSave(0); SettingsSave(0);
} }

View File

@ -1,7 +1,7 @@
/* /*
sonoff.h - Master header file for Sonoff-Tasmota sonoff.h - Master header file for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -46,6 +46,7 @@ typedef unsigned long power_t; // Power (Relay) type
#define MAX_PULSETIMERS 8 // Max number of supported pulse timers #define MAX_PULSETIMERS 8 // Max number of supported pulse timers
#define MAX_FRIENDLYNAMES 4 // Max number of Friendly names #define MAX_FRIENDLYNAMES 4 // Max number of Friendly names
#define MAX_DOMOTICZ_IDX 4 // Max number of Domoticz device, key and switch indices #define MAX_DOMOTICZ_IDX 4 // Max number of Domoticz device, key and switch indices
#define MAX_DOMOTICZ_SNS_IDX 12 // Max number of Domoticz sensors indices
#define MODULE SONOFF_BASIC // [Module] Select default model #define MODULE SONOFF_BASIC // [Module] Select default model
@ -132,7 +133,7 @@ enum LightTypes {LT_BASIC, LT_PWM1, LT_PWM2, LT_PWM3, LT_PWM4, LT_PWM5, LT_PWM6,
enum LichtSubtypes {LST_NONE, LST_SINGLE, LST_COLDWARM, LST_RGB, LST_RGBW, LST_RGBWC}; enum LichtSubtypes {LST_NONE, LST_SINGLE, LST_COLDWARM, LST_RGB, LST_RGBW, LST_RGBWC};
enum LichtSchemes {LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX}; enum LichtSchemes {LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX};
enum XsnsFunctions {FUNC_XSNS_INIT, FUNC_XSNS_EVERY_SECOND, FUNC_XSNS_PREP, FUNC_XSNS_JSON_APPEND, FUNC_XSNS_WEB, FUNC_XSNS_SAVE_STATE}; enum XsnsFunctions {FUNC_INIT, FUNC_EVERY_50_MSECOND, FUNC_EVERY_SECOND, FUNC_PREP_BEFORE_TELEPERIOD, FUNC_JSON_APPEND, FUNC_WEB_APPEND, FUNC_SAVE_BEFORE_RESTART};
const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 }; const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 };

View File

@ -1,7 +1,7 @@
/* /*
sonoff.ino - Sonoff-Tasmota firmware for iTead Sonoff, Wemos and NodeMCU hardware sonoff.ino - Sonoff-Tasmota firmware for iTead Sonoff, Wemos and NodeMCU hardware
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -25,8 +25,8 @@
- Select IDE Tools - Flash Size: "1M (no SPIFFS)" - Select IDE Tools - Flash Size: "1M (no SPIFFS)"
====================================================*/ ====================================================*/
#define VERSION 0x050A0002 #define VERSION 0x050A0003
#define VERSION_STRING "5.10.0b" // Would be great to have a macro that fills this from VERSION ... #define VERSION_STRING "5.10.0c" // Would be great to have a macro that fills this from VERSION ...
// Location specific includes // Location specific includes
#include "sonoff.h" // Enumaration used in user_config.h #include "sonoff.h" // Enumaration used in user_config.h
@ -1100,15 +1100,15 @@ void MqttDataCallback(char* topic, byte* data, unsigned int data_len)
else if (CMND_MODULES == command_code) { else if (CMND_MODULES == command_code) {
for (byte i = 0; i < MAXMODULE; i++) { for (byte i = 0; i < MAXMODULE; i++) {
if (!jsflg) { if (!jsflg) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_MODULES "%d\":\""), lines); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_MODULES "%d\":["), lines);
} else { } else {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data);
} }
jsflg = 1; jsflg = 1;
snprintf_P(stemp1, sizeof(stemp1), kModules[i].name); snprintf_P(stemp1, sizeof(stemp1), kModules[i].name);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%d (%s)"), mqtt_data, i +1, stemp1); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"%d (%s)\""), mqtt_data, i +1, stemp1);
if ((strlen(mqtt_data) > 200) || (i == MAXMODULE -1)) { if ((strlen(mqtt_data) > 300) || (i == MAXMODULE -1)) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"}"), mqtt_data); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s]}"), mqtt_data);
MqttPublishPrefixTopic_P(5, type); MqttPublishPrefixTopic_P(5, type);
jsflg = 0; jsflg = 0;
lines++; lines++;
@ -1149,15 +1149,15 @@ void MqttDataCallback(char* topic, byte* data, unsigned int data_len)
else if (CMND_GPIOS == command_code) { else if (CMND_GPIOS == command_code) {
for (byte i = 0; i < GPIO_SENSOR_END; i++) { for (byte i = 0; i < GPIO_SENSOR_END; i++) {
if (!jsflg) { if (!jsflg) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_GPIOS "%d\":\""), lines); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_GPIOS "%d\":["), lines);
} else { } else {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data);
} }
jsflg = 1; jsflg = 1;
snprintf_P(stemp1, sizeof(stemp1), kSensors[i]); snprintf_P(stemp1, sizeof(stemp1), kSensors[i]);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%d (%s)"), mqtt_data, i, stemp1); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"%d (%s)\""), mqtt_data, i, stemp1);
if ((strlen(mqtt_data) > 200) || (i == GPIO_SENSOR_END -1)) { if ((strlen(mqtt_data) > 300) || (i == GPIO_SENSOR_END -1)) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"}"), mqtt_data); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s]}"), mqtt_data);
MqttPublishPrefixTopic_P(5, type); MqttPublishPrefixTopic_P(5, type);
jsflg = 0; jsflg = 0;
lines++; lines++;
@ -1812,7 +1812,7 @@ boolean MqttShowSensor()
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_SWITCH "%d\":\"%s\""), mqtt_data, i +1, GetStateText(swm ^ lastwallswitch[i])); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_SWITCH "%d\":\"%s\""), mqtt_data, i +1, GetStateText(swm ^ lastwallswitch[i]));
} }
} }
XsnsCall(FUNC_XSNS_JSON_APPEND); XsnsCall(FUNC_JSON_APPEND);
boolean json_data_available = (strlen(mqtt_data) - json_data_start); boolean json_data_available = (strlen(mqtt_data) - json_data_start);
if (strstr_P(mqtt_data, PSTR(D_TEMPERATURE))) { if (strstr_P(mqtt_data, PSTR(D_TEMPERATURE))) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_TEMPERATURE_UNIT "\":\"%c\""), mqtt_data, TempUnit()); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_TEMPERATURE_UNIT "\":\"%c\""), mqtt_data, TempUnit());
@ -1871,7 +1871,7 @@ void PerformEverySecond()
if (Settings.tele_period) { if (Settings.tele_period) {
tele_period++; tele_period++;
if (tele_period == Settings.tele_period -1) { if (tele_period == Settings.tele_period -1) {
XsnsCall(FUNC_XSNS_PREP); XsnsCall(FUNC_PREP_BEFORE_TELEPERIOD);
} }
if (tele_period >= Settings.tele_period) { if (tele_period >= Settings.tele_period) {
tele_period = 0; tele_period = 0;
@ -1887,7 +1887,7 @@ void PerformEverySecond()
} }
} }
XsnsCall(FUNC_XSNS_EVERY_SECOND); XsnsCall(FUNC_EVERY_SECOND);
if ((2 == RtcTime.minute) && latest_uptime_flag) { if ((2 == RtcTime.minute) && latest_uptime_flag) {
latest_uptime_flag = false; latest_uptime_flag = false;
@ -2220,6 +2220,8 @@ void StateLoop()
LightAnimate(); LightAnimate();
} }
XsnsCall(FUNC_EVERY_50_MSECOND);
/*-------------------------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------------------------*\
* Every 0.2 second * Every 0.2 second
\*-------------------------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------------------------*/
@ -2521,17 +2523,20 @@ void GpioInit()
#endif // USE_I2C #endif // USE_I2C
devices_present = 1; devices_present = 1;
light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0
if (Settings.flag.pwm_control) { if (Settings.flag.pwm_control) {
light_type = LT_BASIC;
for (byte i = 0; i < MAX_PWMS; i++) { for (byte i = 0; i < MAX_PWMS; i++) {
if (pin[GPIO_PWM1 +i] < 99) { if (pin[GPIO_PWM1 +i] < 99) {
light_type++; // Use Dimmer/Color control for all PWM as SetOption15 = 1 light_type++; // Use Dimmer/Color control for all PWM as SetOption15 = 1
} }
} }
} }
if (SONOFF_BRIDGE == Settings.module) { if (SONOFF_BRIDGE == Settings.module) {
baudrate = 19200; baudrate = 19200;
} }
if (SONOFF_DUAL == Settings.module) { if (SONOFF_DUAL == Settings.module) {
devices_present = 2; devices_present = 2;
baudrate = 19200; baudrate = 19200;
@ -2544,11 +2549,6 @@ void GpioInit()
devices_present = 0; devices_present = 0;
baudrate = 19200; baudrate = 19200;
} }
else if ((H801 == Settings.module) || (MAGICHOME == Settings.module) || (ARILUX_LC01 == Settings.module) || (ARILUX_LC11 == Settings.module)) { // PWM RGBCW led
if (!Settings.flag.pwm_control) {
light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0
}
}
else if (SONOFF_BN == Settings.module) { // PWM Single color led (White) else if (SONOFF_BN == Settings.module) { // PWM Single color led (White)
light_type = LT_PWM1; light_type = LT_PWM1;
} }
@ -2572,6 +2572,7 @@ void GpioInit()
} }
} }
} }
for (byte i = 0; i < MAX_KEYS; i++) { for (byte i = 0; i < MAX_KEYS; i++) {
if (pin[GPIO_KEY1 +i] < 99) { if (pin[GPIO_KEY1 +i] < 99) {
pinMode(pin[GPIO_KEY1 +i], (16 == pin[GPIO_KEY1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP); pinMode(pin[GPIO_KEY1 +i], (16 == pin[GPIO_KEY1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP);
@ -2599,7 +2600,7 @@ void GpioInit()
if (light_type) { // Any Led light under Dimmer/Color control if (light_type) { // Any Led light under Dimmer/Color control
LightInit(); LightInit();
} else { } else {
for (byte i = 0; i < MAX_PWMS; i++) { for (byte i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
if (pin[GPIO_PWM1 +i] < 99) { if (pin[GPIO_PWM1 +i] < 99) {
pinMode(pin[GPIO_PWM1 +i], OUTPUT); pinMode(pin[GPIO_PWM1 +i], OUTPUT);
analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]);
@ -2623,8 +2624,6 @@ void GpioInit()
} }
#endif // USE_IR_RECEIVE #endif // USE_IR_RECEIVE
#endif // USE_IR_REMOTE #endif // USE_IR_REMOTE
// energy_flg = (((pin[GPIO_HLW_SEL] < 99) && (pin[GPIO_HLW_CF1] < 99) && (pin[GPIO_HLW_CF] < 99)) || ((pin[GPIO_PZEM_RX] < 99) && (pin[GPIO_PZEM_TX])));
} }
extern "C" { extern "C" {

View File

@ -1,7 +1,7 @@
/* /*
sonoff_post.h - Post header file for Sonoff-Tasmota sonoff_post.h - Post header file for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,7 @@
/* /*
sonoff_template.h - template settings for Sonoff-Tasmota sonoff_template.h - template settings for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -82,6 +82,8 @@ enum UserSelectablePins {
GPIO_MHZ_RXD, // MH-Z19 Serial interface GPIO_MHZ_RXD, // MH-Z19 Serial interface
GPIO_PZEM_TX, // PZEM004T Serial interface GPIO_PZEM_TX, // PZEM004T Serial interface
GPIO_PZEM_RX, // PZEM004T Serial interface GPIO_PZEM_RX, // PZEM004T Serial interface
GPIO_SAIR_TX, // SenseAir Serial interface
GPIO_SAIR_RX, // SenseAir Serial interface
GPIO_SENSOR_END }; GPIO_SENSOR_END };
// Text in webpage Module Parameters and commands GPIOS and GPIO // Text in webpage Module Parameters and commands GPIOS and GPIO
@ -145,7 +147,9 @@ const char kSensors[GPIO_SENSOR_END][9] PROGMEM = {
D_SENSOR_MHZ_TX, D_SENSOR_MHZ_TX,
D_SENSOR_MHZ_RX, D_SENSOR_MHZ_RX,
D_SENSOR_PZEM_TX, D_SENSOR_PZEM_TX,
D_SENSOR_PZEM_RX D_SENSOR_PZEM_RX,
D_SENSOR_SAIR_TX,
D_SENSOR_SAIR_RX
}; };
// Programmer selectable GPIO functionality offset by user selectable GPIOs // Programmer selectable GPIO functionality offset by user selectable GPIOs
@ -205,6 +209,7 @@ enum SupportedModules {
ARILUX_LC01, ARILUX_LC01,
ARILUX_LC11, ARILUX_LC11,
SONOFF_DUAL_R2, SONOFF_DUAL_R2,
ARILUX_LC06,
MAXMODULE }; MAXMODULE };
/********************************************************************************************/ /********************************************************************************************/
@ -256,6 +261,7 @@ const uint8_t kNiceList[MAXMODULE] PROGMEM = {
H801, H801,
MAGICHOME, MAGICHOME,
ARILUX_LC01, ARILUX_LC01,
ARILUX_LC06,
ARILUX_LC11, ARILUX_LC11,
HUAFAN_SS, HUAFAN_SS,
KMC_70011, KMC_70011,
@ -743,7 +749,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
GPIO_REL1, // GPIO14 Relay GPIO_REL1, // GPIO14 Relay
0, 0, 0 0, 0, 0
}, },
{ "Arilux", // Arilux AL-LC01 (ESP8285) - https://www.banggood.com/nl/ARILUX-AL-LC01-Super-Mini-LED-WIFI-Smart-RGB-Controller-For-RGB-LED-Strip-Light-DC-9-12V-p-1058603.html { "Arilux LC01", // Arilux AL-LC01 (ESP8285) - https://www.banggood.com/nl/ARILUX-AL-LC01-Super-Mini-LED-WIFI-Smart-RGB-Controller-For-RGB-LED-Strip-Light-DC-9-12V-p-1058603.html
// (PwmFrequency 1111Hz) // (PwmFrequency 1111Hz)
GPIO_KEY1, // GPIO00 Optional Button GPIO_KEY1, // GPIO00 Optional Button
0, 0,
@ -786,7 +792,38 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
GPIO_REL1, // GPIO12 Relay 1 (0 = Off, 1 = On) GPIO_REL1, // GPIO12 Relay 1 (0 = Off, 1 = On)
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off)
0, 0, 0, 0 0, 0, 0, 0
},
{ "Arilux LC06", // Arilux AL-LC06 (ESP8285) - https://www.banggood.com/ARILUX-AL-LC06-LED-WIFI-Smartphone-Controller-Romote-5-Channels-DC12-24V-For-RGBWW-Strip-light-p-1061476.html
GPIO_KEY1, // GPIO00 Optional Button
0,
GPIO_USER, // GPIO02 Empty pad
0,
GPIO_USER, // GPIO04 W2 - PWM5
0,
0, 0, 0, 0, 0, 0, // Flash connection
GPIO_PWM2, // GPIO12 RGB LED Green
GPIO_PWM3, // GPIO13 RGB LED Blue
GPIO_PWM1, // GPIO14 RGB LED Red
GPIO_USER, // GPIO15 RGBW LED White
0, 0
} }
}; };
/*
Optionals
{ "Xenon 3CH", // Xenon 3CH (ESP8266) - (#1128)
0, 0, 0,
GPIO_KEY2, // GPIO03 Serial TXD and Optional sensor
GPIO_REL2, // GPIO04 Relay 2
GPIO_KEY3, // GPIO05 Input 2
0, 0, 0, 0, 0, 0, // Flash connection
GPIO_KEY1, // GPIO12 Key input
GPIO_REL1, // GPIO13 Relay 1
0,
GPIO_REL3, // GPIO15 Relay 3
0, 0
}
*/
#endif // _SONOFF_TEMPLATE_H_ #endif // _SONOFF_TEMPLATE_H_

View File

@ -1,7 +1,7 @@
/* /*
support.ino - support for Sonoff-Tasmota support.ino - support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -1337,15 +1337,15 @@ boolean Xsns02(byte function)
if (pin[GPIO_ADC0] < 99) { if (pin[GPIO_ADC0] < 99) {
switch (function) { switch (function) {
// case FUNC_XSNS_INIT: // case FUNC_INIT:
// break; // break;
// case FUNC_XSNS_PREP: // case FUNC_PREP_BEFORE_TELEPERIOD:
// break; // break;
case FUNC_XSNS_JSON_APPEND: case FUNC_JSON_APPEND:
AdcShow(1); AdcShow(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
AdcShow(0); AdcShow(0);
break; break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER

View File

@ -1,7 +1,7 @@
/* /*
user_config.h - user specific configuration for Sonoff-Tasmota user_config.h - user specific configuration for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -163,7 +163,7 @@
// -- Sensor code selection ----------------------- // -- Sensor code selection -----------------------
#define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices #define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices
//#define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k3 code) //#define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code)
// WARNING: Select none for default one DS18B20 sensor or enable one of the following two options for multiple sensors // WARNING: Select none for default one DS18B20 sensor or enable one of the following two options for multiple sensors
//#define USE_DS18x20 // Optional for more than one DS18x20 sensors with id sort, single scan and read retry (+1k3 code) //#define USE_DS18x20 // Optional for more than one DS18x20 sensors with id sort, single scan and read retry (+1k3 code)
@ -192,9 +192,8 @@
#define USE_WS2812_CTYPE 1 // WS2812 Color type (0 - RGB, 1 - GRB, 2 - RGBW, 3 - GRBW) #define USE_WS2812_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_WS2812_DMA // DMA supports only GPIO03 (= Serial RXD) (+1k mem). When USE_WS2812_DMA is enabled expect Exceptions on Pow
//#define USE_MHZ19_HARD_SERIAL // Add support for MH-Z19 CO2 sensor using hardware serial interface at 9600 bps on GPIO1/3 only (+1k1 code) //#define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k 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_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code)
//#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) #define USE_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code)

View File

@ -1,7 +1,7 @@
/* /*
user_config_override.h - user configuration overrides user_config.h for Sonoff-Tasmota user_config_override.h - user configuration overrides user_config.h for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,7 @@
/* /*
webserver.ino - webserver for Sonoff-Tasmota webserver.ino - webserver for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -511,7 +511,7 @@ void HandleAjaxStatusRefresh()
String page = ""; String page = "";
mqtt_data[0] = '\0'; mqtt_data[0] = '\0';
XsnsCall(FUNC_XSNS_WEB); XsnsCall(FUNC_WEB_APPEND);
if (strlen(mqtt_data)) { if (strlen(mqtt_data)) {
page += FPSTR(HTTP_TABLE100); page += FPSTR(HTTP_TABLE100);
page += mqtt_data; page += mqtt_data;

View File

@ -1,7 +1,7 @@
/* /*
xdrv_domoticz.ino - domoticz support for Sonoff-Tasmota xdrv_domoticz.ino - domoticz support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -40,10 +40,14 @@ enum DomoticzCommands {
const char kDomoticzCommands[] PROGMEM = const char kDomoticzCommands[] PROGMEM =
D_CMND_IDX "|" D_CMND_KEYIDX "|" D_CMND_SWITCHIDX "|" D_CMND_SENSORIDX "|" D_CMND_UPDATETIMER ; D_CMND_IDX "|" D_CMND_KEYIDX "|" D_CMND_SWITCHIDX "|" D_CMND_SENSORIDX "|" D_CMND_UPDATETIMER ;
enum DomoticzSensors {DZ_TEMP, DZ_TEMP_HUM, DZ_TEMP_HUM_BARO, DZ_POWER_ENERGY, DZ_ILLUMINANCE, DZ_COUNT, DZ_VOLTAGE, DZ_CURRENT, DZ_MAX_SENSORS}; enum DomoticzSensors {DZ_TEMP, DZ_TEMP_HUM, DZ_TEMP_HUM_BARO, DZ_POWER_ENERGY, DZ_ILLUMINANCE, DZ_COUNT, DZ_VOLTAGE, DZ_CURRENT, DZ_AIRQUALITY, DZ_MAX_SENSORS};
#if MAX_DOMOTICZ_SNS_IDX < DZ_MAX_SENSORS
#error "Domoticz: Too many sensors or change settings.h layout"
#endif
const char kDomoticzSensors[] PROGMEM = const char kDomoticzSensors[] PROGMEM =
D_DOMOTICZ_TEMP "|" D_DOMOTICZ_TEMP_HUM "|" D_DOMOTICZ_TEMP_HUM_BARO "|" D_DOMOTICZ_POWER_ENERGY "|" D_DOMOTICZ_ILLUMINANCE "|" D_DOMOTICZ_COUNT "|" D_DOMOTICZ_VOLTAGE "|" D_DOMOTICZ_CURRENT ; D_DOMOTICZ_TEMP "|" D_DOMOTICZ_TEMP_HUM "|" D_DOMOTICZ_TEMP_HUM_BARO "|" D_DOMOTICZ_POWER_ENERGY "|" D_DOMOTICZ_ILLUMINANCE "|" D_DOMOTICZ_COUNT "|" D_DOMOTICZ_VOLTAGE "|" D_DOMOTICZ_CURRENT "|" D_DOMOTICZ_AIRQUALITY ;
const char S_JSON_DOMOTICZ_COMMAND_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_DOMOTICZ "%s%d\":%d}"; const char S_JSON_DOMOTICZ_COMMAND_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_DOMOTICZ "%s%d\":%d}";
@ -254,6 +258,19 @@ boolean DomoticzButton(byte key, byte device, byte state, byte svalflg)
/*********************************************************************************************\ /*********************************************************************************************\
* Sensors * Sensors
*
* Source : https://www.domoticz.com/wiki/Domoticz_API/JSON_URL%27s
* https://www.domoticz.com/wiki/MQTT
*
* Percentage, Barometric, Air Quality:
* {\"idx\":%d,\"nvalue\":%s}, Idx, Value
*
* Humidity:
* {\"idx\":%d,\"nvalue\":%s,\"svalue\":\"%s\"}, Idx, Humidity, HumidityStatus
*
* All other:
* {\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}, Idx, Value(s)
*
\*********************************************************************************************/ \*********************************************************************************************/
uint8_t DomoticzHumidityState(char *hum) uint8_t DomoticzHumidityState(char *hum)
@ -268,8 +285,11 @@ void DomoticzSensor(byte idx, char *data)
char dmess[64]; char dmess[64];
memcpy(dmess, mqtt_data, sizeof(dmess)); memcpy(dmess, mqtt_data, sizeof(dmess));
snprintf_P(mqtt_data, sizeof(dmess), PSTR("{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}"), if (DZ_AIRQUALITY == idx) {
Settings.domoticz_sensor_idx[idx], data); snprintf_P(mqtt_data, sizeof(dmess), PSTR("{\"idx\":%d,\"nvalue\":%s}"), Settings.domoticz_sensor_idx[idx], data);
} else {
snprintf_P(mqtt_data, sizeof(dmess), PSTR("{\"idx\":%d,\"nvalue\":0,\"svalue\":\"%s\"}"), Settings.domoticz_sensor_idx[idx], data);
}
MqttPublish(domoticz_in_topic); MqttPublish(domoticz_in_topic);
memcpy(mqtt_data, dmess, sizeof(dmess)); memcpy(mqtt_data, dmess, sizeof(dmess));
} }
@ -353,6 +373,7 @@ void HandleDomoticzConfiguration()
void DomoticzSaveSettings() void DomoticzSaveSettings()
{ {
char stemp[20]; char stemp[20];
char ssensor_indices[6 * MAX_DOMOTICZ_SNS_IDX];
for (byte i = 0; i < MAX_DOMOTICZ_IDX; i++) { for (byte i = 0; i < MAX_DOMOTICZ_IDX; i++) {
snprintf_P(stemp, sizeof(stemp), PSTR("r%d"), i +1); snprintf_P(stemp, sizeof(stemp), PSTR("r%d"), i +1);
@ -362,20 +383,19 @@ void DomoticzSaveSettings()
snprintf_P(stemp, sizeof(stemp), PSTR("s%d"), i +1); snprintf_P(stemp, sizeof(stemp), PSTR("s%d"), i +1);
Settings.domoticz_switch_idx[i] = (!strlen(WebServer->arg(stemp).c_str())) ? 0 : atoi(WebServer->arg(stemp).c_str()); Settings.domoticz_switch_idx[i] = (!strlen(WebServer->arg(stemp).c_str())) ? 0 : atoi(WebServer->arg(stemp).c_str());
} }
ssensor_indices[0] = '\0';
for (byte i = 0; i < DZ_MAX_SENSORS; i++) { for (byte i = 0; i < DZ_MAX_SENSORS; i++) {
snprintf_P(stemp, sizeof(stemp), PSTR("l%d"), i +1); snprintf_P(stemp, sizeof(stemp), PSTR("l%d"), i +1);
Settings.domoticz_sensor_idx[i] = (!strlen(WebServer->arg(stemp).c_str())) ? 0 : atoi(WebServer->arg(stemp).c_str()); Settings.domoticz_sensor_idx[i] = (!strlen(WebServer->arg(stemp).c_str())) ? 0 : atoi(WebServer->arg(stemp).c_str());
snprintf_P(ssensor_indices, sizeof(ssensor_indices), PSTR("%s%s%d"), ssensor_indices, (strlen(ssensor_indices)) ? "," : "", Settings.domoticz_sensor_idx[i]);
} }
Settings.domoticz_update_timer = (!strlen(WebServer->arg("ut").c_str())) ? DOMOTICZ_UPDATE_TIMER : atoi(WebServer->arg("ut").c_str()); Settings.domoticz_update_timer = (!strlen(WebServer->arg("ut").c_str())) ? DOMOTICZ_UPDATE_TIMER : atoi(WebServer->arg("ut").c_str());
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DOMOTICZ D_CMND_IDX " %d, %d, %d, %d, " D_CMND_UPDATETIMER " %d"),
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DOMOTICZ D_CMND_IDX " %d,%d,%d,%d, " D_CMND_KEYIDX " %d,%d,%d,%d, " D_CMND_SWITCHIDX " %d,%d,%d,%d, " D_CMND_SENSORIDX " %s, " D_CMND_UPDATETIMER " %d"),
Settings.domoticz_relay_idx[0], Settings.domoticz_relay_idx[1], Settings.domoticz_relay_idx[2], Settings.domoticz_relay_idx[3], Settings.domoticz_relay_idx[0], Settings.domoticz_relay_idx[1], Settings.domoticz_relay_idx[2], Settings.domoticz_relay_idx[3],
Settings.domoticz_update_timer);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DOMOTICZ D_CMND_KEYIDX " %d, %d, %d, %d, " D_CMND_SWITCHIDX " %d, %d, %d, %d, " D_CMND_SENSORIDX " %d, %d, %d, %d, %d, %d, %d, %d"),
Settings.domoticz_key_idx[0], Settings.domoticz_key_idx[1], Settings.domoticz_key_idx[2], Settings.domoticz_key_idx[3], Settings.domoticz_key_idx[0], Settings.domoticz_key_idx[1], Settings.domoticz_key_idx[2], Settings.domoticz_key_idx[3],
Settings.domoticz_switch_idx[0], Settings.domoticz_switch_idx[1], Settings.domoticz_switch_idx[2], Settings.domoticz_switch_idx[3], Settings.domoticz_switch_idx[0], Settings.domoticz_switch_idx[1], Settings.domoticz_switch_idx[2], Settings.domoticz_switch_idx[3],
Settings.domoticz_sensor_idx[0], Settings.domoticz_sensor_idx[1], Settings.domoticz_sensor_idx[2], Settings.domoticz_sensor_idx[3], ssensor_indices, Settings.domoticz_update_timer);
Settings.domoticz_sensor_idx[4], Settings.domoticz_sensor_idx[5], Settings.domoticz_sensor_idx[6], Settings.domoticz_sensor_idx[7]);
AddLog(LOG_LEVEL_INFO); AddLog(LOG_LEVEL_INFO);
} }
#endif // USE_WEBSERVER #endif // USE_WEBSERVER

View File

@ -1,7 +1,7 @@
/* /*
xdrv_irremote.ino - infra red support for Sonoff-Tasmota xdrv_irremote.ino - infra red support for Sonoff-Tasmota
Copyright (C) 2017 Heiko Krupp, Lazar Obradovic and Theo Arends Copyright (C) 2018 Heiko Krupp, Lazar Obradovic and Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,7 @@
/* /*
xdrv_light.ino - PWM, WS2812 and sonoff led support for Sonoff-Tasmota xdrv_light.ino - PWM, WS2812 and sonoff led support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,7 @@
/* /*
xdrv_snfbridge.ino - sonoff RF bridge 433 support for Sonoff-Tasmota xdrv_snfbridge.ino - sonoff RF bridge 433 support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,7 @@
/* /*
xdrv_wemohue.ino - wemo and hue support for Sonoff-Tasmota xdrv_wemohue.ino - wemo and hue support for Sonoff-Tasmota
Copyright (C) 2017 Heiko Krupp and Theo Arends Copyright (C) 2018 Heiko Krupp and Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -268,6 +268,17 @@ const char WEMO_EVENTSERVICE_XML[] PROGMEM =
"</action>" "</action>"
"</scpd>\r\n" "</scpd>\r\n"
"\r\n"; "\r\n";
const char WEMO_RESPONSE_STATE_SOAP[] PROGMEM =
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">"
"<s:Body>"
"<u:SetBinaryStateResponse xmlns:u=\"urn:Belkin:service:basicevent:1\">"
"<BinaryState>{x1</BinaryState>"
"</u:SetBinaryStateResponse>"
"</s:Body>"
"</s:Envelope>\r\n"
"\r\n";
const char WEMO_SETUP_XML[] PROGMEM = const char WEMO_SETUP_XML[] PROGMEM =
"<?xml version=\"1.0\"?>" "<?xml version=\"1.0\"?>"
"<root>" "<root>"
@ -299,17 +310,21 @@ void HandleUpnpEvent()
{ {
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_BASIC_EVENT)); AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_BASIC_EVENT));
String request = WebServer->arg(0); String request = WebServer->arg(0);
String state_xml = FPSTR(WEMO_RESPONSE_STATE_SOAP);
//differentiate get and set state
if (request.indexOf(F("SetBinaryState")) > 0) { if (request.indexOf(F("SetBinaryState")) > 0) {
if (request.indexOf(F("State>1</Binary")) > 0) { if (request.indexOf(F("State>1</Binary")) > 0) {
// ExecuteCommandPower(1, 1);
ExecuteCommandPower(devices_present, 1); ExecuteCommandPower(devices_present, 1);
} }
if (request.indexOf(F("State>0</Binary")) > 0) { else if (request.indexOf(F("State>0</Binary")) > 0) {
// ExecuteCommandPower(1, 0);
ExecuteCommandPower(devices_present, 0); ExecuteCommandPower(devices_present, 0);
} }
} }
WebServer->send(200, FPSTR(HDR_CTYPE_PLAIN), ""); else if(request.indexOf(F("GetBinaryState")) > 0){
state_xml.replace("Set", "Get");
}
state_xml.replace("{x1", String(bitRead(power, devices_present -1)));
WebServer->send(200, FPSTR(HDR_CTYPE_XML), state_xml);
} }
void HandleUpnpService() void HandleUpnpService()

View File

@ -1,7 +1,7 @@
/* /*
xdrv_ws2812.ino - ws2812 led string support for Sonoff-Tasmota xdrv_ws2812.ino - ws2812 led string support for Sonoff-Tasmota
Copyright (C) 2017 Heiko Krupp and Theo Arends Copyright (C) 2018 Heiko Krupp and Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by

View File

@ -1,7 +1,7 @@
/* /*
xsns_01_counter.ino - Counter sensors (water meters, electricity meters etc.) sensor support for Sonoff-Tasmota xsns_01_counter.ino - Counter sensors (water meters, electricity meters etc.) sensor support for Sonoff-Tasmota
Copyright (C) 2017 Maarten Damen and Theo Arends Copyright (C) 2018 Maarten Damen and Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -130,20 +130,18 @@ boolean Xsns01(byte function)
boolean result = false; boolean result = false;
switch (function) { switch (function) {
case FUNC_XSNS_INIT: case FUNC_INIT:
CounterInit(); CounterInit();
break; break;
// case FUNC_XSNS_PREP: case FUNC_JSON_APPEND:
// break;
case FUNC_XSNS_JSON_APPEND:
CounterShow(1); CounterShow(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
CounterShow(0); CounterShow(0);
break; break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER
case FUNC_XSNS_SAVE_STATE: case FUNC_SAVE_BEFORE_RESTART:
CounterSaveState(); CounterSaveState();
break; break;
} }

View File

@ -1,7 +1,7 @@
/* /*
xsns_03_energy.ino - HLW8012 (Sonoff Pow) and PZEM004T energy sensor support for Sonoff-Tasmota xsns_03_energy.ino - HLW8012 (Sonoff Pow) and PZEM004T energy sensor support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -41,7 +41,6 @@ const char kEnergyCommands[] PROGMEM =
D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|" D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|"
D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW ; D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW ;
bool energy_power_factor_ready = false;
float energy_voltage = 0; // 123.1 V float energy_voltage = 0; // 123.1 V
float energy_current = 0; // 123.123 A float energy_current = 0; // 123.123 A
float energy_power = 0; // 123.1 W float energy_power = 0; // 123.1 W
@ -208,25 +207,6 @@ void HlwEvery200ms()
hlw_cf1_summed_pulse_length = 0; hlw_cf1_summed_pulse_length = 0;
hlw_cf1_pulse_counter = 0; hlw_cf1_pulse_counter = 0;
} }
/*
energy_power = 0;
if (hlw_cf_pulse_length && (power &1) && !hlw_load_off) {
hlw_w = (HLW_PREF * Settings.hlw_power_calibration) / hlw_cf_pulse_length;
energy_power = (float)hlw_w / 10;
}
energy_voltage = 0;
if (hlw_cf1_voltage_pulse_length && (power &1)) { // If powered on always provide voltage
hlw_u = (HLW_UREF * Settings.hlw_voltage_calibration) / hlw_cf1_voltage_pulse_length;
energy_voltage = (float)hlw_u / 10;
}
energy_current = 0;
if (hlw_cf1_current_pulse_length && energy_power) { // No current if no power being consumed
hlw_i = (HLW_IREF * Settings.hlw_current_calibration) / hlw_cf1_current_pulse_length;
energy_current = (float)hlw_i / 1000;
}
*/
energy_power_factor_ready = true;
} }
void HlwInit() void HlwInit()
@ -269,119 +249,9 @@ void HlwInit()
* Based on: PZEM004T library https://github.com/olehs/PZEM004T * Based on: PZEM004T library https://github.com/olehs/PZEM004T
\*********************************************************************************************/ \*********************************************************************************************/
#define PZEM_BAUD_RATE 9600 #include <TasmotaSerial.h>
/*********************************************************************************************\ TasmotaSerial *PzemSerial;
* Subset SoftwareSerial
\*********************************************************************************************/
#define PZEM_SERIAL_BUFFER_SIZE 20
#define PZEM_SERIAL_WAIT { while (ESP.getCycleCount() -start < wait) optimistic_yield(1); wait += pzem_serial_bit_time; }
uint8_t pzem_serial_rx_pin;
uint8_t pzem_serial_tx_pin;
uint8_t pzem_serial_in_pos = 0;
uint8_t pzem_serial_out_pos = 0;
uint8_t pzem_serial_buffer[PZEM_SERIAL_BUFFER_SIZE];
unsigned long pzem_serial_bit_time;
unsigned long pzem_serial_bit_time_start;
bool PzemSerialValidGpioPin(uint8_t pin) {
return (pin >= 0 && pin <= 5) || (pin >= 9 && pin <= 10) || (pin >= 12 && pin <= 15);
}
bool PzemSerial(uint8_t receive_pin, uint8_t transmit_pin)
{
if (!((PzemSerialValidGpioPin(receive_pin)) && (PzemSerialValidGpioPin(transmit_pin) || transmit_pin == 16))) {
return false;
}
pzem_serial_rx_pin = receive_pin;
pinMode(pzem_serial_rx_pin, INPUT);
attachInterrupt(pzem_serial_rx_pin, PzemSerialRxRead, FALLING);
pzem_serial_tx_pin = transmit_pin;
pinMode(pzem_serial_tx_pin, OUTPUT);
digitalWrite(pzem_serial_tx_pin, 1);
pzem_serial_bit_time = ESP.getCpuFreqMHz() *1000000 /PZEM_BAUD_RATE; // 8333
pzem_serial_bit_time_start = pzem_serial_bit_time + pzem_serial_bit_time /3 -500; // 10610 ICACHE_RAM_ATTR start delay
// pzem_serial_bit_time_start = pzem_serial_bit_time; // Non ICACHE_RAM_ATTR start delay (experimental)
return true;
}
int PzemSerialRead() {
if (pzem_serial_in_pos == pzem_serial_out_pos) {
return -1;
}
int ch = pzem_serial_buffer[pzem_serial_out_pos];
pzem_serial_out_pos = (pzem_serial_out_pos +1) % PZEM_SERIAL_BUFFER_SIZE;
return ch;
}
int PzemSerialAvailable() {
int avail = pzem_serial_in_pos - pzem_serial_out_pos;
if (avail < 0) {
avail += PZEM_SERIAL_BUFFER_SIZE;
}
return avail;
}
size_t PzemSerialTxWrite(uint8_t b)
{
unsigned long wait = pzem_serial_bit_time;
digitalWrite(pzem_serial_tx_pin, HIGH);
unsigned long start = ESP.getCycleCount();
// Start bit;
digitalWrite(pzem_serial_tx_pin, LOW);
PZEM_SERIAL_WAIT;
for (int i = 0; i < 8; i++) {
digitalWrite(pzem_serial_tx_pin, (b & 1) ? HIGH : LOW);
PZEM_SERIAL_WAIT;
b >>= 1;
}
// Stop bit
digitalWrite(pzem_serial_tx_pin, HIGH);
PZEM_SERIAL_WAIT;
return 1;
}
size_t PzemSerialWrite(const uint8_t *buffer, size_t size = 1) {
size_t n = 0;
while(size--) {
n += PzemSerialTxWrite(*buffer++);
}
return n;
}
//void PzemSerialRxRead() ICACHE_RAM_ATTR; // Add 215 bytes to iram usage
void PzemSerialRxRead() {
// Advance the starting point for the samples but compensate for the
// initial delay which occurs before the interrupt is delivered
unsigned long wait = pzem_serial_bit_time_start;
unsigned long start = ESP.getCycleCount();
uint8_t rec = 0;
for (int i = 0; i < 8; i++) {
PZEM_SERIAL_WAIT;
rec >>= 1;
if (digitalRead(pzem_serial_rx_pin)) {
rec |= 0x80;
}
}
// Stop bit
PZEM_SERIAL_WAIT;
// Store the received value in the buffer unless we have an overflow
int next = (pzem_serial_in_pos +1) % PZEM_SERIAL_BUFFER_SIZE;
if (next != pzem_serial_out_pos) {
pzem_serial_buffer[pzem_serial_in_pos] = rec;
pzem_serial_in_pos = next;
}
// Must clear this bit in the interrupt register,
// it gets set even when interrupts are disabled
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << pzem_serial_rx_pin);
}
/*********************************************************************************************/
#define PZEM_VOLTAGE (uint8_t)0xB0 #define PZEM_VOLTAGE (uint8_t)0xB0
#define RESP_VOLTAGE (uint8_t)0xA0 #define RESP_VOLTAGE (uint8_t)0xA0
@ -401,11 +271,7 @@ void PzemSerialRxRead() {
#define PZEM_POWER_ALARM (uint8_t)0xB5 #define PZEM_POWER_ALARM (uint8_t)0xB5
#define RESP_POWER_ALARM (uint8_t)0xA5 #define RESP_POWER_ALARM (uint8_t)0xA5
#define RESPONSE_SIZE sizeof(PZEMCommand)
#define RESPONSE_DATA_SIZE RESPONSE_SIZE - 2
#define PZEM_DEFAULT_READ_TIMEOUT 500 #define PZEM_DEFAULT_READ_TIMEOUT 500
#define PZEM_ERROR_VALUE -1.0
struct PZEMCommand { struct PZEMCommand {
uint8_t command; uint8_t command;
@ -416,52 +282,16 @@ struct PZEMCommand {
IPAddress pzem_ip(192, 168, 1, 1); IPAddress pzem_ip(192, 168, 1, 1);
float PZEM004T_voltage_rcv() uint8_t PzemCrc(uint8_t *data)
{ {
uint8_t data[RESPONSE_DATA_SIZE]; uint16_t crc = 0;
for (uint8_t i = 0; i < sizeof(PZEMCommand) -1; i++) {
if (!PZEM004T_recieve(RESP_VOLTAGE, data)) { crc += *data++;
return PZEM_ERROR_VALUE;
} }
return (data[0] << 8) + data[1] + (data[2] / 10.0); // 65535.x V return (uint8_t)(crc & 0xFF);
} }
float PZEM004T_current_rcv() void PzemSend(uint8_t cmd)
{
uint8_t data[RESPONSE_DATA_SIZE];
if (!PZEM004T_recieve(RESP_CURRENT, data)) {
return PZEM_ERROR_VALUE;
}
return (data[0] << 8) + data[1] + (data[2] / 100.0); // 65535.xx A
}
float PZEM004T_power_rcv()
{
uint8_t data[RESPONSE_DATA_SIZE];
if (!PZEM004T_recieve(RESP_POWER, data)) {
return PZEM_ERROR_VALUE;
}
return (data[0] << 8) + data[1]; // 65535 W
}
float PZEM004T_energy_rcv()
{
uint8_t data[RESPONSE_DATA_SIZE];
if (!PZEM004T_recieve(RESP_ENERGY, data)) {
return PZEM_ERROR_VALUE;
}
return ((uint32_t)data[0] << 16) + ((uint16_t)data[1] << 8) + data[2]; // 16777215 Wh
}
bool PZEM004T_setAddress_rcv()
{
return PZEM004T_recieve(RESP_SET_ADDRESS, 0);
}
void PZEM004T_send(uint8_t cmd)
{ {
PZEMCommand pzem; PZEMCommand pzem;
@ -472,117 +302,89 @@ void PZEM004T_send(uint8_t cmd)
pzem.data = 0; pzem.data = 0;
uint8_t *bytes = (uint8_t*)&pzem; uint8_t *bytes = (uint8_t*)&pzem;
pzem.crc = PZEM004T_crc(bytes, sizeof(pzem) - 1); pzem.crc = PzemCrc(bytes);
while (PzemSerialAvailable()) { PzemSerial->write(bytes, sizeof(pzem));
PzemSerialRead();
}
PzemSerialWrite(bytes, sizeof(pzem));
} }
bool PZEM004T_isReady() bool PzemReceiveReady()
{ {
return PzemSerialAvailable() >= RESPONSE_SIZE; return PzemSerial->available() >= sizeof(PZEMCommand);
} }
bool PZEM004T_recieve(uint8_t resp, uint8_t *data) bool PzemRecieve(uint8_t resp, float *data)
{ {
uint8_t buffer[RESPONSE_SIZE]; uint8_t buffer[sizeof(PZEMCommand)];
unsigned long startTime = millis(); unsigned long start = millis();
uint8_t len = 0; uint8_t len = 0;
// while ((len < RESPONSE_SIZE) && (millis() - startTime < PZEM_DEFAULT_READ_TIMEOUT)) { while ((len < sizeof(PZEMCommand)) && (millis() - start < PZEM_DEFAULT_READ_TIMEOUT)) {
while ((len < RESPONSE_SIZE) && (millis() - startTime < PZEM_DEFAULT_READ_TIMEOUT)) { if (PzemSerial->available() > 0) {
if (PzemSerialAvailable() > 0) { uint8_t c = (uint8_t)PzemSerial->read();
uint8_t c = (uint8_t)PzemSerialRead();
if (!c && !len) { if (!c && !len) {
continue; // skip 0 at startup continue; // skip 0 at startup
} }
buffer[len++] = c; buffer[len++] = c;
} }
// yield(); // do background netw tasks while blocked for IO (prevents ESP watchdog trigger) - This triggers Watchdog!!!
} }
if (len != RESPONSE_SIZE) { if (len != sizeof(PZEMCommand)) {
// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Pzem comms timeout"));
return false; return false;
} }
if (buffer[6] != PZEM004T_crc(buffer, len - 1)) { if (buffer[6] != PzemCrc(buffer)) {
// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Pzem crc error"));
return false; return false;
} }
if (buffer[0] != resp) { if (buffer[0] != resp) {
// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Pzem bad response"));
return false; return false;
} }
if (data) {
for (int i = 0; i < RESPONSE_DATA_SIZE; i++) {
data[i] = buffer[1 + i];
}
}
switch (resp) {
case RESP_VOLTAGE:
*data = (float)(buffer[1] << 8) + buffer[2] + (buffer[3] / 10.0); // 65535.x V
break;
case RESP_CURRENT:
*data = (float)(buffer[1] << 8) + buffer[2] + (buffer[3] / 100.0); // 65535.xx A
break;
case RESP_POWER:
*data = (float)(buffer[1] << 8) + buffer[2]; // 65535 W
break;
case RESP_ENERGY:
*data = (float)((uint32_t)buffer[1] << 16) + ((uint16_t)buffer[2] << 8) + buffer[3]; // 16777215 Wh
break;
}
return true; return true;
} }
uint8_t PZEM004T_crc(uint8_t *data, uint8_t sz)
{
uint16_t crc = 0;
for (uint8_t i = 0; i < sz; i++) {
crc += *data++;
}
return (uint8_t)(crc & 0xFF);
}
/*********************************************************************************************/ /*********************************************************************************************/
typedef enum const uint8_t pzem_commands[] { PZEM_SET_ADDRESS, PZEM_VOLTAGE, PZEM_CURRENT, PZEM_POWER, PZEM_ENERGY };
{ const uint8_t pzem_responses[] { RESP_SET_ADDRESS, RESP_VOLTAGE, RESP_CURRENT, RESP_POWER, RESP_ENERGY };
SET_ADDRESS,
READ_VOLTAGE,
READ_CURRENT,
READ_POWER,
READ_ENERGY,
} PZEMReadStates;
PZEMReadStates pzem_read_state = SET_ADDRESS; uint8_t pzem_read_state = 0;
uint8_t pzem_sendRetry = 0;
byte pzem_sendRetry = 0;
void PzemEvery200ms() void PzemEvery200ms()
{ {
bool dataReady = PZEM004T_isReady(); bool data_ready = PzemReceiveReady();
if (dataReady) { if (data_ready) {
float pzem_value; float value = 0;
if (PzemRecieve(pzem_responses[pzem_read_state], &value)) {
switch (pzem_read_state) { switch (pzem_read_state) {
case SET_ADDRESS: case 1:
if (PZEM004T_setAddress_rcv()) { energy_voltage = value; // 230.2V
pzem_read_state = READ_VOLTAGE;
}
break; break;
case READ_VOLTAGE: case 2:
pzem_value = PZEM004T_voltage_rcv(); energy_current = value; // 17.32A
if (pzem_value != PZEM_ERROR_VALUE) {
energy_voltage = pzem_value; // 230.2V
pzem_read_state = READ_CURRENT;
}
break; break;
case READ_CURRENT: case 3:
pzem_value = PZEM004T_current_rcv(); energy_power = value; // 20W
if (pzem_value != PZEM_ERROR_VALUE) {
energy_current = pzem_value; // 17.32A
pzem_read_state = READ_POWER;
}
break; break;
case READ_POWER: case 4:
pzem_value = PZEM004T_power_rcv(); energy_total = value / 1000; // 99999Wh
if (pzem_value != PZEM_ERROR_VALUE) {
energy_power = pzem_value; // 20W
energy_power_factor_ready = true;
pzem_read_state = READ_ENERGY;
}
break;
case READ_ENERGY:
pzem_value = PZEM004T_energy_rcv();
if (pzem_value != PZEM_ERROR_VALUE) {
energy_total = pzem_value / 1000; // 99999Wh
if (!energy_startup) { if (!energy_startup) {
if (energy_total < energy_start) { if (energy_total < energy_start) {
energy_start = energy_total; energy_start = energy_total;
@ -591,32 +393,18 @@ void PzemEvery200ms()
energy_kWhtoday = (energy_total - energy_start) * 100000000; energy_kWhtoday = (energy_total - energy_start) * 100000000;
energy_daily = (float)energy_kWhtoday / 100000000; energy_daily = (float)energy_kWhtoday / 100000000;
} }
pzem_read_state = READ_VOLTAGE;
}
break; break;
} }
pzem_read_state++;
if (5 == pzem_read_state) {
pzem_read_state = 1;
}
}
} }
if (0 == pzem_sendRetry || dataReady) { if (0 == pzem_sendRetry || data_ready) {
pzem_sendRetry = 5; pzem_sendRetry = 5;
PzemSend(pzem_commands[pzem_read_state]);
switch (pzem_read_state) {
case SET_ADDRESS:
PZEM004T_send(PZEM_SET_ADDRESS);
break;
case READ_VOLTAGE:
PZEM004T_send(PZEM_VOLTAGE);
break;
case READ_CURRENT:
PZEM004T_send(PZEM_CURRENT);
break;
case READ_POWER:
PZEM004T_send(PZEM_POWER);
break;
case READ_ENERGY:
PZEM004T_send(PZEM_ENERGY);
break;
}
} }
else { else {
pzem_sendRetry--; pzem_sendRetry--;
@ -625,7 +413,8 @@ void PzemEvery200ms()
bool PzemInit() bool PzemInit()
{ {
return PzemSerial(pin[GPIO_PZEM_RX], pin[GPIO_PZEM_TX]); PzemSerial = new TasmotaSerial(pin[GPIO_PZEM_RX], pin[GPIO_PZEM_TX]);
return PzemSerial->begin();
} }
/********************************************************************************************/ /********************************************************************************************/
@ -677,14 +466,14 @@ void Energy200ms()
#endif // USE_PZEM004T #endif // USE_PZEM004T
} }
if (energy_power_factor_ready && energy_voltage && energy_current && energy_power) { float power_factor = 0;
energy_power_factor_ready = false; if (energy_voltage && energy_current && energy_power) {
float power_factor = energy_power / (energy_voltage * energy_current); power_factor = energy_power / (energy_voltage * energy_current);
if (power_factor > 1) { if (power_factor > 1) {
power_factor = 1; power_factor = 1;
} }
energy_power_factor = power_factor;
} }
energy_power_factor = power_factor;
} }
void EnergySaveState() void EnergySaveState()
@ -1144,23 +933,21 @@ boolean Xsns03(byte function)
if (energy_flg) { if (energy_flg) {
switch (function) { switch (function) {
case FUNC_XSNS_INIT: case FUNC_INIT:
EnergyInit(); EnergyInit();
break; break;
case FUNC_XSNS_EVERY_SECOND: case FUNC_EVERY_SECOND:
EnergyMarginCheck(); EnergyMarginCheck();
break; break;
// case FUNC_XSNS_PREP: case FUNC_JSON_APPEND:
// break;
case FUNC_XSNS_JSON_APPEND:
EnergyShow(1); EnergyShow(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
EnergyShow(0); EnergyShow(0);
break; break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER
case FUNC_XSNS_SAVE_STATE: case FUNC_SAVE_BEFORE_RESTART:
EnergySaveState(); EnergySaveState();
break; break;
} }

View File

@ -1,7 +1,7 @@
/* /*
xsns_04_snfsc.ino - sonoff SC support for Sonoff-Tasmota xsns_04_snfsc.ino - sonoff SC support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -148,16 +148,14 @@ boolean Xsns04(byte function)
if (SONOFF_SC == Settings.module) { if (SONOFF_SC == Settings.module) {
switch (function) { switch (function) {
case FUNC_XSNS_INIT: case FUNC_INIT:
SonoffScInit(); SonoffScInit();
break; break;
// case FUNC_XSNS_PREP: case FUNC_JSON_APPEND:
// break;
case FUNC_XSNS_JSON_APPEND:
SonoffScShow(1); SonoffScShow(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
SonoffScShow(0); SonoffScShow(0);
break; break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER

View File

@ -1,7 +1,7 @@
/* /*
xsns_05_ds18b20.ino - DS18B20 temperature sensor support for Sonoff-Tasmota xsns_05_ds18b20.ino - DS18B20 temperature sensor support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -219,17 +219,17 @@ boolean Xsns05(byte function)
if (pin[GPIO_DSB] < 99) { if (pin[GPIO_DSB] < 99) {
switch (function) { switch (function) {
case FUNC_XSNS_INIT: case FUNC_INIT:
Ds18x20Init(); Ds18x20Init();
break; break;
case FUNC_XSNS_PREP: case FUNC_PREP_BEFORE_TELEPERIOD:
Ds18x20Convert(); // Start conversion, takes up to one second Ds18x20Convert(); // Start conversion, takes up to one second
break; break;
case FUNC_XSNS_JSON_APPEND: case FUNC_JSON_APPEND:
Ds18b20Show(1); Ds18b20Show(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
Ds18b20Show(0); Ds18b20Show(0);
Ds18x20Convert(); // Start conversion, takes up to one second Ds18x20Convert(); // Start conversion, takes up to one second
break; break;

View File

@ -1,7 +1,7 @@
/* /*
xsns_05_ds18x20.ino - DS18x20 temperature sensor support for Sonoff-Tasmota xsns_05_ds18x20.ino - DS18x20 temperature sensor support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -401,17 +401,17 @@ boolean Xsns05(byte function)
if (pin[GPIO_DSB] < 99) { if (pin[GPIO_DSB] < 99) {
switch (function) { switch (function) {
case FUNC_XSNS_INIT: case FUNC_INIT:
Ds18x20Init(); Ds18x20Init();
break; break;
case FUNC_XSNS_PREP: case FUNC_PREP_BEFORE_TELEPERIOD:
Ds18x20Convert(); // Start conversion, takes up to one second Ds18x20Convert(); // Start conversion, takes up to one second
break; break;
case FUNC_XSNS_JSON_APPEND: case FUNC_JSON_APPEND:
Ds18x20Show(1); Ds18x20Show(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
Ds18x20Show(0); Ds18x20Show(0);
Ds18x20Convert(); // Start conversion, takes up to one second Ds18x20Convert(); // Start conversion, takes up to one second
break; break;

View File

@ -1,7 +1,7 @@
/* /*
xsns_05_ds18x20_legacy.ino - DS18x20 temperature sensor support for Sonoff-Tasmota xsns_05_ds18x20_legacy.ino - DS18x20 temperature sensor support for Sonoff-Tasmota
Copyright (C) 2017 Heiko Krupp and Theo Arends Copyright (C) 2018 Heiko Krupp and Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -219,18 +219,18 @@ boolean Xsns05(byte function)
if (pin[GPIO_DSB] < 99) { if (pin[GPIO_DSB] < 99) {
switch (function) { switch (function) {
case FUNC_XSNS_INIT: case FUNC_INIT:
Ds18x20Init(); Ds18x20Init();
break; break;
case FUNC_XSNS_PREP: case FUNC_PREP_BEFORE_TELEPERIOD:
Ds18x20Search(); // Check for changes in sensors number Ds18x20Search(); // Check for changes in sensors number
Ds18x20Convert(); // Start Conversion, takes up to one second Ds18x20Convert(); // Start Conversion, takes up to one second
break; break;
case FUNC_XSNS_JSON_APPEND: case FUNC_JSON_APPEND:
Ds18x20Show(1); Ds18x20Show(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
Ds18x20Show(0); Ds18x20Show(0);
break; break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER

View File

@ -1,7 +1,7 @@
/* /*
xsns_06_dht.ino - DHTxx, AM23xx and SI7021 temperature and humidity sensor support for Sonoff-Tasmota xsns_06_dht.ino - DHTxx, AM23xx and SI7021 temperature and humidity sensor support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -253,17 +253,17 @@ boolean Xsns06(byte function)
if (dht_flg) { if (dht_flg) {
switch (function) { switch (function) {
case FUNC_XSNS_INIT: case FUNC_INIT:
DhtInit(); DhtInit();
break; break;
case FUNC_XSNS_PREP: case FUNC_PREP_BEFORE_TELEPERIOD:
DhtReadPrep(); DhtReadPrep();
break; break;
case FUNC_XSNS_JSON_APPEND: case FUNC_JSON_APPEND:
DhtShow(1); DhtShow(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
DhtShow(0); DhtShow(0);
break; break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER

View File

@ -1,7 +1,7 @@
/* /*
xsns_07_sht1x.ino - SHT1x temperature and sensor support for Sonoff-Tasmota xsns_07_sht1x.ino - SHT1x temperature and sensor support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -223,16 +223,14 @@ boolean Xsns07(byte function)
if (i2c_flg) { if (i2c_flg) {
switch (function) { switch (function) {
// case FUNC_XSNS_INIT: case FUNC_PREP_BEFORE_TELEPERIOD:
// break;
case FUNC_XSNS_PREP:
ShtDetect(); ShtDetect();
break; break;
case FUNC_XSNS_JSON_APPEND: case FUNC_JSON_APPEND:
ShtShow(1); ShtShow(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
ShtShow(0); ShtShow(0);
break; break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER

View File

@ -1,7 +1,7 @@
/* /*
xsns_08_htu21.ino - HTU21 temperature and humidity sensor support for Sonoff-Tasmota xsns_08_htu21.ino - HTU21 temperature and humidity sensor support for Sonoff-Tasmota
Copyright (C) 2017 Heiko Krupp and Theo Arends Copyright (C) 2018 Heiko Krupp and Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -284,16 +284,14 @@ boolean Xsns08(byte function)
if (i2c_flg) { if (i2c_flg) {
switch (function) { switch (function) {
// case FUNC_XSNS_INIT: case FUNC_PREP_BEFORE_TELEPERIOD:
// break;
case FUNC_XSNS_PREP:
HtuDetect(); HtuDetect();
break; break;
case FUNC_XSNS_JSON_APPEND: case FUNC_JSON_APPEND:
HtuShow(1); HtuShow(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
HtuShow(0); HtuShow(0);
break; break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER

View File

@ -1,7 +1,7 @@
/* /*
xsns_09_bmp.ino - BMP pressure, temperature, humidity and gas sensor support for Sonoff-Tasmota xsns_09_bmp.ino - BMP pressure, temperature, humidity and gas sensor support for Sonoff-Tasmota
Copyright (C) 2017 Heiko Krupp and Theo Arends Copyright (C) 2018 Heiko Krupp and Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -502,19 +502,17 @@ boolean Xsns09(byte function)
if (i2c_flg) { if (i2c_flg) {
switch (function) { switch (function) {
// case FUNC_XSNS_INIT: case FUNC_PREP_BEFORE_TELEPERIOD:
// break;
case FUNC_XSNS_PREP:
BmpDetect(); BmpDetect();
#ifdef USE_BME680 #ifdef USE_BME680
Bme680PerformReading(); Bme680PerformReading();
#endif // USE_BME680 #endif // USE_BME680
break; break;
case FUNC_XSNS_JSON_APPEND: case FUNC_JSON_APPEND:
BmpShow(1); BmpShow(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
BmpShow(0); BmpShow(0);
#ifdef USE_BME680 #ifdef USE_BME680
Bme680PerformReading(); Bme680PerformReading();

View File

@ -1,7 +1,7 @@
/* /*
xsns_10_bh1750.ino - BH1750 ambient light sensor support for Sonoff-Tasmota xsns_10_bh1750.ino - BH1750 ambient light sensor support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -99,16 +99,14 @@ boolean Xsns10(byte function)
if (i2c_flg) { if (i2c_flg) {
switch (function) { switch (function) {
// case FUNC_XSNS_INIT: case FUNC_PREP_BEFORE_TELEPERIOD:
// break;
case FUNC_XSNS_PREP:
Bh1750Detect(); Bh1750Detect();
break; break;
case FUNC_XSNS_JSON_APPEND: case FUNC_JSON_APPEND:
Bh1750Show(1); Bh1750Show(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
Bh1750Show(0); Bh1750Show(0);
break; break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER

View File

@ -1,7 +1,7 @@
/* /*
xsns_11_veml6070.ino - VEML6070 ultra violet light sensor support for Sonoff-Tasmota xsns_11_veml6070.ino - VEML6070 ultra violet light sensor support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -104,16 +104,14 @@ boolean Xsns11(byte function)
if (i2c_flg) { if (i2c_flg) {
switch (function) { switch (function) {
// case FUNC_XSNS_INIT: case FUNC_PREP_BEFORE_TELEPERIOD:
// break;
case FUNC_XSNS_PREP:
Veml6070Detect(); Veml6070Detect();
break; break;
case FUNC_XSNS_JSON_APPEND: case FUNC_JSON_APPEND:
Veml6070Show(1); Veml6070Show(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
Veml6070Show(0); Veml6070Show(0);
break; break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER

View File

@ -1,7 +1,7 @@
/* /*
xsns_12_ads1115_ada.ino - ADS1115 A/D Converter support for Sonoff-Tasmota xsns_12_ads1115_ada.ino - ADS1115 A/D Converter support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -218,16 +218,14 @@ boolean Xsns12(byte function)
if (i2c_flg) { if (i2c_flg) {
switch (function) { switch (function) {
// case FUNC_XSNS_INIT: case FUNC_PREP_BEFORE_TELEPERIOD:
// break;
case FUNC_XSNS_PREP:
Ads1115Detect(); Ads1115Detect();
break; break;
case FUNC_XSNS_JSON_APPEND: case FUNC_JSON_APPEND:
Ads1115Show(1); Ads1115Show(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
Ads1115Show(0); Ads1115Show(0);
break; break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER

View File

@ -1,7 +1,7 @@
/* /*
xsns_12_ads1115.ino - ADS1x15 A/D Converter support for Sonoff-Tasmota xsns_12_ads1115.ino - ADS1x15 A/D Converter support for Sonoff-Tasmota
Copyright (C) 2017 Stefan Bode and Theo Arends Copyright (C) 2018 Stefan Bode and Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -138,16 +138,14 @@ boolean Xsns12(byte function)
if (i2c_flg) { if (i2c_flg) {
switch (function) { switch (function) {
// case FUNC_XSNS_INIT: case FUNC_PREP_BEFORE_TELEPERIOD:
// break;
case FUNC_XSNS_PREP:
Ads1115Detect(); Ads1115Detect();
break; break;
case FUNC_XSNS_JSON_APPEND: case FUNC_JSON_APPEND:
Ads1115Show(1); Ads1115Show(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
Ads1115Show(0); Ads1115Show(0);
break; break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER

View File

@ -1,7 +1,7 @@
/* /*
xsns_13_ina219.ino - INA219 Current Sensor support for Sonoff-Tasmota xsns_13_ina219.ino - INA219 Current Sensor support for Sonoff-Tasmota
Copyright (C) 2017 Stefan Bode and Theo Arends Copyright (C) 2018 Stefan Bode and Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -219,16 +219,14 @@ boolean Xsns13(byte function)
if (i2c_flg) { if (i2c_flg) {
switch (function) { switch (function) {
// case FUNC_XSNS_INIT: case FUNC_PREP_BEFORE_TELEPERIOD:
// break;
case FUNC_XSNS_PREP:
Ina219Detect(); Ina219Detect();
break; break;
case FUNC_XSNS_JSON_APPEND: case FUNC_JSON_APPEND:
Ina219Show(1); Ina219Show(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
Ina219Show(0); Ina219Show(0);
break; break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER

View File

@ -1,7 +1,7 @@
/* /*
xsns_14_sht3x.ino - SHT3X temperature and humidity sensor support for Sonoff-Tasmota xsns_14_sht3x.ino - SHT3X temperature and humidity sensor support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -125,17 +125,17 @@ boolean Xsns14(byte function)
if (i2c_flg) { if (i2c_flg) {
switch (function) { switch (function) {
case FUNC_XSNS_INIT: case FUNC_INIT:
Sht3xDetect(); Sht3xDetect();
break; break;
case FUNC_XSNS_PREP: case FUNC_PREP_BEFORE_TELEPERIOD:
Sht3xConvert(); Sht3xConvert();
break; break;
case FUNC_XSNS_JSON_APPEND: case FUNC_JSON_APPEND:
Sht3xShow(1); Sht3xShow(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
Sht3xShow(0); Sht3xShow(0);
Sht3xConvert(); Sht3xConvert();
break; break;

View File

@ -1,7 +1,7 @@
/* /*
xsns_14_sht3x.ino - SHT3X temperature and humidity sensor support for Sonoff-Tasmota xsns_14_sht3x.ino - SHT3X temperature and humidity sensor support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -122,16 +122,14 @@ boolean Xsns14(byte function)
if (i2c_flg) { if (i2c_flg) {
switch (function) { switch (function) {
case FUNC_XSNS_INIT: case FUNC_INIT:
Sht3xDetect(); Sht3xDetect();
break; break;
// case FUNC_XSNS_PREP: case FUNC_JSON_APPEND:
// break;
case FUNC_XSNS_JSON_APPEND:
Sht3xShow(1); Sht3xShow(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
Sht3xShow(0); Sht3xShow(0);
break; break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER

View File

@ -1,7 +1,7 @@
/* /*
xsns_14_sht3x.ino - SHT3X temperature and humidity sensor support for Sonoff-Tasmota xsns_14_sht3x.ino - SHT3X temperature and humidity sensor support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -132,17 +132,17 @@ boolean Xsns14(byte function)
if (i2c_flg) { if (i2c_flg) {
switch (function) { switch (function) {
case FUNC_XSNS_INIT: case FUNC_INIT:
Sht3xDetect(); Sht3xDetect();
break; break;
case FUNC_XSNS_PREP: case FUNC_PREP_BEFORE_TELEPERIOD:
Sht3xConvert(); Sht3xConvert();
break; break;
case FUNC_XSNS_JSON_APPEND: case FUNC_JSON_APPEND:
Sht3xShow(1); Sht3xShow(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
Sht3xShow(0); Sht3xShow(0);
Sht3xConvert(); Sht3xConvert();
break; break;

265
sonoff/xsns_15_mhz19.ino Normal file
View File

@ -0,0 +1,265 @@
/*
xsns_15_mhz19.ino - MH-Z19(B) CO2 sensor support for Sonoff-Tasmota
Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef USE_MHZ19
/*********************************************************************************************\
* MH-Z19 - CO2 sensor
*
* Adapted from EspEasy plugin P049 by Dmitry (rel22 ___ inbox.ru)
**********************************************************************************************
* Filter usage
*
* Select filter usage on low stability readings
\*********************************************************************************************/
enum MhzFilterOptions {MHZ19_FILTER_OFF, MHZ19_FILTER_OFF_ALLSAMPLES, MHZ19_FILTER_FAST, MHZ19_FILTER_MEDIUM, MHZ19_FILTER_SLOW};
#define MHZ19_FILTER_OPTION MHZ19_FILTER_FAST
/*********************************************************************************************\
* Source: http://www.winsen-sensor.com/d/files/infrared-gas-sensor/mh-z19b-co2-ver1_0.pdf
*
* Automatic Baseline Correction (ABC logic function)
*
* ABC logic function refers to that sensor itself do zero point judgment and automatic calibration procedure
* intelligently after a continuous operation period. The automatic calibration cycle is every 24 hours after powered on.
*
* The zero point of automatic calibration is 400ppm.
*
* This function is usually suitable for indoor air quality monitor such as offices, schools and homes,
* not suitable for greenhouse, farm and refrigeratory where this function should be off.
*
* Please do zero calibration timely, such as manual or commend calibration.
\*********************************************************************************************/
#define MHZ19_ABC_ENABLE 1 // Automatic Baseline Correction (0 = off, 1 = on (default))
/*********************************************************************************************/
#include <TasmotaSerial.h>
TasmotaSerial *MhzSerial;
#define MHZ19_READ_TIMEOUT 500 // Must be way less than 1000
#define MHZ19_RETRY_COUNT 8
const char kMhzTypes[] PROGMEM = "MHZ19|MHZ19B";
const uint8_t mhz_cmnd_read_ppm[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
const uint8_t mhz_cmnd_abc_enable[9] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6};
const uint8_t mhz_cmnd_abc_disable[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86};
uint8_t mhz_type = 1;
uint16_t mhz_last_ppm = 0;
uint8_t mhz_filter = MHZ19_FILTER_OPTION;
bool mhz_abc_enable = MHZ19_ABC_ENABLE;
bool mhz_abc_must_apply = false;
char mhz_types[7];
float mhz_temperature = 0;
uint8_t mhz_timer = 0;
uint8_t mhz_retry = MHZ19_RETRY_COUNT;
uint8_t mhz_state = 0;
/*********************************************************************************************/
bool MhzCheckAndApplyFilter(uint16_t ppm, uint8_t s)
{
if (1 == s) {
return false; // S==1 => "A" version sensor bootup, do not use values.
}
if (mhz_last_ppm < 400 || mhz_last_ppm > 5000) {
// Prevent unrealistic values during start-up with filtering enabled.
// Just assume the entered value is correct.
mhz_last_ppm = ppm;
return true;
}
int32_t difference = ppm - mhz_last_ppm;
if (s > 0 && s < 64 && mhz_filter != MHZ19_FILTER_OFF) {
// Not the "B" version of the sensor, S value is used.
// S==0 => "B" version, else "A" version
// The S value is an indication of the stability of the reading.
// S == 64 represents a stable reading and any lower value indicates (unusual) fast change.
// Now we increase the delay filter for low values of S and increase response time when the
// value is more stable.
// This will make the reading useful in more turbulent environments,
// where the sensor would report more rapid change of measured values.
difference *= s;
difference /= 64;
}
if (MHZ19_FILTER_OFF == mhz_filter) {
if (s != 0 && s != 64) {
return false;
}
} else {
difference >>= (mhz_filter -1);
}
mhz_last_ppm = static_cast<uint16_t>(mhz_last_ppm + difference);
return true;
}
void Mhz50ms()
{
mhz_state++;
if (4 == mhz_state) { // Every 200 mSec
mhz_state = 0;
uint8_t mhz_response[9];
mhz_timer++;
if (6 == mhz_timer) { // MH-Z19 measuring cycle takes 1005 +5% ms
mhz_timer = 0;
MhzSerial->write(mhz_cmnd_read_ppm, 9);
}
if (1 == mhz_timer) {
if (mhz_retry) {
mhz_retry--;
if (!mhz_retry) {
mhz_last_ppm = 0;
mhz_temperature = 0;
}
}
unsigned long start = millis();
uint8_t counter = 0;
while (((millis() - start) < MHZ19_READ_TIMEOUT) && (counter < 9)) {
if (MhzSerial->available() > 0) {
mhz_response[counter++] = MhzSerial->read();
}
}
if (counter < 9) {
// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 comms timeout"));
return;
}
byte crc = 0;
for (uint8_t i = 1; i < 8; i++) {
crc += mhz_response[i];
}
crc = 255 - crc;
crc++;
if (mhz_response[8] != crc) {
// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 crc error"));
return;
}
if (0xFF != mhz_response[0] || 0x86 != mhz_response[1]) {
// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 bad response"));
return;
}
uint16_t u = (mhz_response[6] << 8) | mhz_response[7];
if (15000 == u) { // During (and only ever at) sensor boot, 'u' is reported as 15000
if (!mhz_abc_enable) {
// After bootup of the sensor the ABC will be enabled.
// Thus only actively disable after bootup.
mhz_abc_must_apply = true;
}
} else {
uint16_t ppm = (mhz_response[2] << 8) | mhz_response[3];
mhz_temperature = ConvertTemp((float)mhz_response[4] - 40);
uint8_t s = mhz_response[5];
mhz_type = (s) ? 1 : 2;
if (MhzCheckAndApplyFilter(ppm, s)) {
mhz_retry = MHZ19_RETRY_COUNT;
if (0 == s || 64 == s) { // Reading is stable.
if (mhz_abc_must_apply) {
mhz_abc_must_apply = false;
if (mhz_abc_enable) {
MhzSerial->write(mhz_cmnd_abc_enable, 9); // Sent sensor ABC Enable
} else {
MhzSerial->write(mhz_cmnd_abc_disable, 9); // Sent sensor ABC Disable
}
}
}
}
}
}
}
}
/*********************************************************************************************/
void MhzInit()
{
mhz_type = 0;
if ((pin[GPIO_MHZ_RXD] < 99) && (pin[GPIO_MHZ_TXD] < 99)) {
MhzSerial = new TasmotaSerial(pin[GPIO_MHZ_RXD], pin[GPIO_MHZ_TXD]);
if (MhzSerial->begin()) {
mhz_type = 1;
}
}
}
void MhzShow(boolean json)
{
char temperature[10];
dtostrfd(mhz_temperature, Settings.flag2.temperature_resolution, temperature);
GetTextIndexed(mhz_types, sizeof(mhz_types), mhz_type -1, kMhzTypes);
if (json) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_CO2 "\":%d,\"" D_TEMPERATURE "\":%s}"), mqtt_data, mhz_types, mhz_last_ppm, temperature);
#ifdef USE_DOMOTICZ
DomoticzSensor(DZ_AIRQUALITY, mhz_last_ppm);
#endif // USE_DOMOTICZ
#ifdef USE_WEBSERVER
} else {
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_CO2, mqtt_data, mhz_types, mhz_last_ppm);
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, mhz_types, temperature, TempUnit());
#endif // USE_WEBSERVER
}
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
#define XSNS_15
boolean Xsns15(byte function)
{
boolean result = false;
if (mhz_type) {
switch (function) {
case FUNC_INIT:
MhzInit();
break;
case FUNC_EVERY_50_MSECOND:
Mhz50ms();
break;
case FUNC_JSON_APPEND:
MhzShow(1);
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_APPEND:
MhzShow(0);
break;
#endif // USE_WEBSERVER
}
}
return result;
}
#endif // USE_MHZ19

View File

@ -1,277 +0,0 @@
/*
xsns_15_mhz.ino - MH-Z19 CO2 sensor support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <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

View File

@ -1,377 +0,0 @@
/*
xsns_15_mhz.ino - MH-Z19 CO2 sensor support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef USE_MHZ19_SOFT_SERIAL
/*********************************************************************************************\
* MH-Z19 - CO2 sensor
*
* Based on EspEasy plugin P049 by Dmitry (rel22 ___ inbox.ru)
**********************************************************************************************
* Filter usage
*
* Select filter usage on low stability readings
\*********************************************************************************************/
enum Mhz19FilterOptions {MHZ19_FILTER_OFF, MHZ19_FILTER_OFF_ALLSAMPLES, MHZ19_FILTER_FAST, MHZ19_FILTER_MEDIUM, MHZ19_FILTER_SLOW};
#define MHZ19_FILTER_OPTION MHZ19_FILTER_FAST
/*********************************************************************************************\
* Source: http://www.winsen-sensor.com/d/files/infrared-gas-sensor/mh-z19b-co2-ver1_0.pdf
*
* Automatic Baseline Correction (ABC logic function)
*
* ABC logic function refers to that sensor itself do zero point judgment and automatic calibration procedure
* intelligently after a continuous operation period. The automatic calibration cycle is every 24 hours after powered on.
*
* The zero point of automatic calibration is 400ppm.
*
* This function is usually suitable for indoor air quality monitor such as offices, schools and homes,
* not suitable for greenhouse, farm and refrigeratory where this function should be off.
*
* Please do zero calibration timely, such as manual or commend calibration.
\*********************************************************************************************/
#define MHZ19_ABC_ENABLE 1 // Automatic Baseline Correction (0 = off, 1 = on (default))
/*********************************************************************************************/
#define MHZ19_BAUDRATE 9600
#define MHZ19_READ_TIMEOUT 600 // Must be way less than 1000
const char kMhz19Types[] PROGMEM = "MHZ19|MHZ19B";
const uint8_t mhz19_cmnd_read_ppm[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
const uint8_t mhz19_cmnd_abc_enable[9] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6};
const uint8_t mhz19_cmnd_abc_disable[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86};
uint8_t mhz19_type = 0;
uint16_t mhz19_last_ppm = 0;
uint8_t mhz19_filter = MHZ19_FILTER_OPTION;
uint8_t mhz19_response[9];
bool mhz19_abc_enable = MHZ19_ABC_ENABLE;
bool mhz19_abc_must_apply = false;
char mhz19_types[7];
/*********************************************************************************************\
* Subset SoftwareSerial
\*********************************************************************************************/
#define MHZ19_SERIAL_BUFFER_SIZE 20
#define MHZ19_SERIAL_WAIT { while (ESP.getCycleCount() -start < wait) optimistic_yield(1); wait += mhz19_serial_bit_time; }
uint8_t mhz19_serial_rx_pin;
uint8_t mhz19_serial_tx_pin;
uint8_t mhz19_serial_in_pos = 0;
uint8_t mhz19_serial_out_pos = 0;
uint8_t mhz19_serial_buffer[MHZ19_SERIAL_BUFFER_SIZE];
unsigned long mhz19_serial_bit_time;
unsigned long mhz19_serial_bit_time_start;
bool Mhz19SerialValidGpioPin(uint8_t pin) {
return (pin >= 0 && pin <= 5) || (pin >= 12 && pin <= 15);
}
bool Mhz19Serial(uint8_t receive_pin, uint8_t transmit_pin)
{
if (!((Mhz19SerialValidGpioPin(receive_pin)) && (Mhz19SerialValidGpioPin(transmit_pin) || transmit_pin == 16))) {
return false;
}
mhz19_serial_rx_pin = receive_pin;
pinMode(mhz19_serial_rx_pin, INPUT);
attachInterrupt(mhz19_serial_rx_pin, Mhz19SerialRxRead, FALLING);
mhz19_serial_tx_pin = transmit_pin;
pinMode(mhz19_serial_tx_pin, OUTPUT);
digitalWrite(mhz19_serial_tx_pin, 1);
mhz19_serial_bit_time = ESP.getCpuFreqMHz() *1000000 /MHZ19_BAUDRATE; // 8333
mhz19_serial_bit_time_start = mhz19_serial_bit_time + mhz19_serial_bit_time /3 -500; // 10610 ICACHE_RAM_ATTR start delay
// mhz19_serial_bit_time_start = mhz19_serial_bit_time; // Non ICACHE_RAM_ATTR start delay (experimental)
return true;
}
int Mhz19SerialRead() {
if (mhz19_serial_in_pos == mhz19_serial_out_pos) {
return -1;
}
int ch = mhz19_serial_buffer[mhz19_serial_out_pos];
mhz19_serial_out_pos = (mhz19_serial_out_pos +1) % MHZ19_SERIAL_BUFFER_SIZE;
return ch;
}
int Mhz19SerialAvailable() {
int avail = mhz19_serial_in_pos - mhz19_serial_out_pos;
if (avail < 0) {
avail += MHZ19_SERIAL_BUFFER_SIZE;
}
return avail;
}
void Mhz19SerialFlush()
{
mhz19_serial_in_pos = 0;
mhz19_serial_out_pos = 0;
}
size_t Mhz19SerialTxWrite(uint8_t b)
{
unsigned long wait = mhz19_serial_bit_time;
digitalWrite(mhz19_serial_tx_pin, HIGH);
unsigned long start = ESP.getCycleCount();
// Start bit;
digitalWrite(mhz19_serial_tx_pin, LOW);
MHZ19_SERIAL_WAIT;
for (int i = 0; i < 8; i++) {
digitalWrite(mhz19_serial_tx_pin, (b & 1) ? HIGH : LOW);
MHZ19_SERIAL_WAIT;
b >>= 1;
}
// Stop bit
digitalWrite(mhz19_serial_tx_pin, HIGH);
MHZ19_SERIAL_WAIT;
return 1;
}
size_t Mhz19SerialWrite(const uint8_t *buffer, size_t size = 1) {
size_t n = 0;
while(size--) {
n += Mhz19SerialTxWrite(*buffer++);
}
return n;
}
void Mhz19SerialRxRead() ICACHE_RAM_ATTR; // Add 215 bytes to iram usage
void Mhz19SerialRxRead() {
// Advance the starting point for the samples but compensate for the
// initial delay which occurs before the interrupt is delivered
unsigned long wait = mhz19_serial_bit_time_start;
unsigned long start = ESP.getCycleCount();
uint8_t rec = 0;
for (int i = 0; i < 8; i++) {
MHZ19_SERIAL_WAIT;
rec >>= 1;
if (digitalRead(mhz19_serial_rx_pin)) {
rec |= 0x80;
}
}
// Stop bit
MHZ19_SERIAL_WAIT;
// Store the received value in the buffer unless we have an overflow
int next = (mhz19_serial_in_pos +1) % MHZ19_SERIAL_BUFFER_SIZE;
if (next != mhz19_serial_out_pos) {
mhz19_serial_buffer[mhz19_serial_in_pos] = rec;
mhz19_serial_in_pos = next;
}
// Must clear this bit in the interrupt register,
// it gets set even when interrupts are disabled
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << mhz19_serial_rx_pin);
}
/*********************************************************************************************/
bool Mhz19CheckAndApplyFilter(uint16_t ppm, uint8_t s)
{
if (1 == s) {
return false; // S==1 => "A" version sensor bootup, do not use values.
}
if (mhz19_last_ppm < 400 || mhz19_last_ppm > 5000) {
// Prevent unrealistic values during start-up with filtering enabled.
// Just assume the entered value is correct.
mhz19_last_ppm = ppm;
return true;
}
int32_t difference = ppm - mhz19_last_ppm;
if (s > 0 && s < 64 && mhz19_filter != MHZ19_FILTER_OFF) {
// Not the "B" version of the sensor, S value is used.
// S==0 => "B" version, else "A" version
// The S value is an indication of the stability of the reading.
// S == 64 represents a stable reading and any lower value indicates (unusual) fast change.
// Now we increase the delay filter for low values of S and increase response time when the
// value is more stable.
// This will make the reading useful in more turbulent environments,
// where the sensor would report more rapid change of measured values.
difference *= s;
difference /= 64;
}
if (MHZ19_FILTER_OFF == mhz19_filter) {
if (s != 0 && s != 64) {
return false;
}
} else {
difference >>= (mhz19_filter -1);
}
mhz19_last_ppm = static_cast<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)
{
Mhz19SerialFlush();
if (Mhz19SerialWrite(mhz19_cmnd_read_ppm, 9) != 9) {
return false; // Unable to send 9 bytes
}
memset(mhz19_response, 0, sizeof(mhz19_response));
uint32_t start = millis();
uint8_t counter = 0;
while (((millis() - start) < MHZ19_READ_TIMEOUT) && (counter < 9)) {
if (Mhz19SerialAvailable() > 0) {
mhz19_response[counter++] = Mhz19SerialRead();
} else {
delay(10);
}
}
if (counter < 9){
return false; // Timeout while trying to read
}
byte crc = 0;
for (uint8_t i = 1; i < 8; i++) {
crc += mhz19_response[i];
}
crc = 255 - crc;
crc++;
/*
// Test data
mhz19_response[0] = 0xFF;
mhz19_response[1] = 0x86;
mhz19_response[2] = 0x12;
mhz19_response[3] = 0x86;
mhz19_response[4] = 64;
// mhz19_response[5] = 32;
mhz19_response[8] = crc;
*/
if (0xFF == mhz19_response[0] && 0x86 == mhz19_response[1] && mhz19_response[8] == crc) {
uint16_t u = (mhz19_response[6] << 8) | mhz19_response[7];
if (15000 == u) { // During (and only ever at) sensor boot, 'u' is reported as 15000
if (!mhz19_abc_enable) {
// After bootup of the sensor the ABC will be enabled.
// Thus only actively disable after bootup.
mhz19_abc_must_apply = true;
}
} else {
uint16_t ppm = (mhz19_response[2] << 8) | mhz19_response[3];
t = ConvertTemp((float)mhz19_response[4] - 40);
uint8_t s = mhz19_response[5];
if (s) {
mhz19_type = 1;
} else {
mhz19_type = 2;
}
if (Mhz19CheckAndApplyFilter(ppm, s)) {
p = mhz19_last_ppm;
if (0 == s || 64 == s) { // Reading is stable.
if (mhz19_abc_must_apply) {
mhz19_abc_must_apply = false;
if (mhz19_abc_enable) {
Mhz19SerialWrite(mhz19_cmnd_abc_enable, 9); // Sent sensor ABC Enable
} else {
Mhz19SerialWrite(mhz19_cmnd_abc_disable, 9); // Sent sensor ABC Disable
}
}
}
status = true;
}
}
}
}
return status;
}
void Mhz19Init()
{
if (Mhz19Serial(pin[GPIO_MHZ_RXD], pin[GPIO_MHZ_TXD])) {
mhz19_type = 1;
}
}
#ifdef USE_WEBSERVER
const char HTTP_SNS_CO2[] PROGMEM =
"%s{s}%s " D_CO2 "{m}%d " D_UNIT_PPM "{e}"; // {s} = <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

View File

@ -1,277 +0,0 @@
/*
xsns_15_mhz.ino - MH-Z19 CO2 sensor support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <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

View File

@ -1,7 +1,7 @@
/* /*
xsns_16_tsl2561.ino - TSL2561 light sensor support for Sonoff-Tasmota xsns_16_tsl2561.ino - TSL2561 light sensor support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -91,16 +91,14 @@ boolean Xsns16(byte function)
if (i2c_flg) { if (i2c_flg) {
switch (function) { switch (function) {
// case FUNC_XSNS_INIT: case FUNC_PREP_BEFORE_TELEPERIOD:
// break;
case FUNC_XSNS_PREP:
Tsl2561Detect(); Tsl2561Detect();
break; break;
case FUNC_XSNS_JSON_APPEND: case FUNC_JSON_APPEND:
Tsl2561Show(1); Tsl2561Show(1);
break; break;
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
case FUNC_XSNS_WEB: case FUNC_WEB_APPEND:
Tsl2561Show(0); Tsl2561Show(0);
break; break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER

248
sonoff/xsns_17_senseair.ino Normal file
View File

@ -0,0 +1,248 @@
/*
xsns_17_senseair.ino - SenseAir CO2 sensor support for Sonoff-Tasmota
Copyright (C) 2018 Theo Arends
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef USE_SENSEAIR
/*********************************************************************************************\
* SenseAir K30, K70 and S8 - CO2 sensor
*
* Adapted from EspEasy plugin P052 by Mikael Trieb (mikael__AT__triebconsulting.se)
\*********************************************************************************************/
#include <TasmotaSerial.h>
TasmotaSerial *SensairSerial;
const char kSenseairTypes[] PROGMEM = "Kx0|S8";
uint8_t senseair_type = 1;
char senseair_types[7];
uint16_t senseair_co2 = 0;
float senseair_temperature = 0;
float senseair_humidity = 0;
uint8_t senseair_state = 0;
/*********************************************************************************************/
void ModbusSend(uint8_t function_code, uint16_t start_address, uint16_t register_count)
{
uint8_t frame[8];
frame[0] = 0xFE; // Any Address
frame[1] = function_code;
frame[2] = (uint8_t)(start_address >> 8);
frame[3] = (uint8_t)(start_address);
frame[4] = (uint8_t)(register_count >> 8);
frame[5] = (uint8_t)(register_count);
uint16_t crc = 0xFFFF;
for (uint8_t pos = 0; pos < sizeof(frame) -2; pos++) {
crc ^= (uint16_t)frame[pos]; // XOR byte into least sig. byte of crc
for (uint8_t i = 8; i != 0; i--) { // Loop over each bit
if ((crc & 0x0001) != 0) { // If the LSB is set
crc >>= 1; // Shift right and XOR 0xA001
crc ^= 0xA001;
}
else { // Else LSB is not set
crc >>= 1; // Just shift right
}
}
}
frame[7] = (uint8_t)((crc >> 8) & 0xFF);
frame[6] = (uint8_t)(crc & 0xFF);
SensairSerial->write(frame, sizeof(frame));
}
bool ModbusReceiveReady()
{
return (SensairSerial->available() >= 5); // 5 - Error frame, 7 - Ok frame
}
uint8_t ModbusReceive(uint16_t *value)
{
uint8_t buffer[7];
uint8_t len = 0;
while (SensairSerial->available() > 0) {
buffer[len++] = (uint8_t)SensairSerial->read();
if (3 == len) {
if (buffer[1] & 0x80) { // fe 84 02 f2 f1
return buffer[2]; // 1 = Illegal Function, 2 = Illegal Data Address, 3 = Illegal Data Value
}
}
}
if (len != sizeof(buffer)) {
return 9; // 9 = Unexpected result
}
*value = (buffer[3] << 8) | buffer[4];
return 0; // 0 = No error
}
/*********************************************************************************************/
const uint8_t start_addresses[] { 0x1A, 0x00, 0x03, 0x04, 0x05, 0x1C, 0x0A };
uint8_t senseair_read_state = 0;
uint8_t senseair_send_retry = 0;
void Senseair50ms() // Every 50 mSec
{
senseair_state++;
if (6 == senseair_state) { // Every 300 mSec
senseair_state = 0;
uint16_t value = 0;
bool data_ready = ModbusReceiveReady();
if (data_ready) {
uint8_t error = ModbusReceive(&value);
if (error) {
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SenseAir response error %d"), error);
AddLog(LOG_LEVEL_DEBUG);
} else {
switch(senseair_read_state) {
case 0: // 0x1A (26) READ_TYPE_LOW - S8: fe 04 02 01 77 ec 92
senseair_type = 2;
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SenseAir type id low %04X"), value);
AddLog(LOG_LEVEL_DEBUG);
break;
case 1: // 0x00 (0) READ_ERRORLOG - fe 04 02 00 00 ad 24
if (value) {
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SenseAir error %04X"), value);
AddLog(LOG_LEVEL_DEBUG);
}
break;
case 2: // 0x03 (3) READ_CO2 - fe 04 02 06 2c af 59
senseair_co2 = value;
break;
case 3: // 0x04 (4) READ_TEMPERATURE - S8: fe 84 02 f2 f1 - Illegal Data Address
senseair_temperature = ConvertTemp((float)value / 100);
break;
case 4: // 0x05 (5) READ_HUMIDITY - S8: fe 84 02 f2 f1 - Illegal Data Address
senseair_humidity = (float)value / 100;
break;
case 5: // 0x1C (28) READ_RELAY_STATE - S8: fe 04 02 01 54 ad 4b - firmware version
{
bool relay_state = value >> 8 & 1;
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SenseAir relay state %d"), relay_state);
AddLog(LOG_LEVEL_DEBUG);
break;
}
case 6: // 0x0A (10) READ_TEMP_ADJUSTMENT - S8: fe 84 02 f2 f1 - Illegal Data Address
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "SenseAir temp adjustment %d"), value);
AddLog(LOG_LEVEL_DEBUG);
break;
}
}
senseair_read_state++;
if (2 == senseair_type) { // S8
if (3 == senseair_read_state) {
senseair_read_state = 1;
}
} else { // K30, K70
if (sizeof(start_addresses) == senseair_read_state) {
senseair_read_state = 1;
}
}
}
if (0 == senseair_send_retry || data_ready) {
senseair_send_retry = 5;
ModbusSend(0x04, (uint16_t)start_addresses[senseair_read_state], 1);
} else {
senseair_send_retry--;
}
}
}
/*********************************************************************************************/
void SenseairInit()
{
senseair_type = 0;
if ((pin[GPIO_SAIR_RX] < 99) && (pin[GPIO_SAIR_TX] < 99)) {
SensairSerial = new TasmotaSerial(pin[GPIO_SAIR_RX], pin[GPIO_SAIR_TX]);
if (SensairSerial->begin()) {
senseair_type = 1;
}
}
}
void SenseairShow(boolean json)
{
char temperature[10];
char humidity[10];
dtostrfd(senseair_temperature, Settings.flag2.temperature_resolution, temperature);
dtostrfd(senseair_humidity, Settings.flag2.temperature_resolution, humidity);
GetTextIndexed(senseair_types, sizeof(senseair_types), senseair_type -1, kSenseairTypes);
if (json) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_CO2 "\":%d"), mqtt_data, senseair_types, senseair_co2);
if (senseair_type != 2) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_TEMPERATURE "\":%s,\"" D_HUMIDITY "\":%s"), mqtt_data, temperature, humidity);
}
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data);
#ifdef USE_DOMOTICZ
DomoticzSensor(DZ_AIRQUALITY, senseair_co2);
#endif // USE_DOMOTICZ
#ifdef USE_WEBSERVER
} else {
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_CO2, mqtt_data, senseair_types, senseair_co2);
if (senseair_type != 2) {
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, senseair_types, temperature, TempUnit());
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_HUM, mqtt_data, senseair_types, humidity);
}
#endif // USE_WEBSERVER
}
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
#define XSNS_17
boolean Xsns17(byte function)
{
boolean result = false;
if (senseair_type) {
switch (function) {
case FUNC_INIT:
SenseairInit();
break;
case FUNC_EVERY_50_MSECOND:
Senseair50ms();
break;
case FUNC_JSON_APPEND:
SenseairShow(1);
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_APPEND:
SenseairShow(0);
break;
#endif // USE_WEBSERVER
}
}
return result;
}
#endif // USE_SENSEAIR

View File

@ -1,7 +1,7 @@
/* /*
xsns_interface.ino - External sensor interface support for Sonoff-Tasmota xsns_interface.ino - External sensor interface support for Sonoff-Tasmota
Copyright (C) 2017 Theo Arends inspired by ESPEasy Copyright (C) 2018 Theo Arends inspired by ESPEasy
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -104,7 +104,10 @@ void XSnsInit()
xsns_func_ptr[xsns_present++] = &Xsns20; xsns_func_ptr[xsns_present++] = &Xsns20;
#endif #endif
XsnsCall(FUNC_XSNS_INIT); // snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_DEBUG "Sensors found %d"), xsns_present);
// AddLog(LOG_LEVEL_DEBUG);
XsnsCall(FUNC_INIT);
} }
/*********************************************************************************************\ /*********************************************************************************************\
@ -115,18 +118,23 @@ boolean XsnsCall(byte Function)
{ {
boolean result = false; boolean result = false;
/*
switch (Function) { switch (Function) {
case FUNC_XSNS_INIT: case FUNC_INIT:
case FUNC_XSNS_EVERY_SECOND: case FUNC_EVERY_50_MSECOND:
case FUNC_XSNS_PREP: case FUNC_EVERY_SECOND:
case FUNC_XSNS_JSON_APPEND: case FUNC_PREP_BEFORE_TELEPERIOD:
case FUNC_XSNS_WEB: case FUNC_JSON_APPEND:
case FUNC_XSNS_SAVE_STATE: case FUNC_WEB_APPEND:
case FUNC_SAVE_BEFORE_RESTART:
*/
for (byte x = 0; x < xsns_present; x++) { for (byte x = 0; x < xsns_present; x++) {
xsns_func_ptr[x](Function); xsns_func_ptr[x](Function);
} }
/*
break; break;
} }
*/
return result; return result;
} }