Merge branch 'arendst/development' into development

This commit is contained in:
reloxx13 2018-07-23 20:30:35 +02:00
commit 97dee49a93
46 changed files with 2039 additions and 158 deletions

View File

@ -12,12 +12,3 @@ script:
- platformio run
before_deploy:
- for file in .pioenvs/*/firmware.bin; do cp $file ${file%/*}.bin; done
deploy:
provider: releases
api_key:
secure: I5x8qJ5+229qJv5jSnNPGERQHl0NSKvrVHdpVzr/HOo2zeZ/Q5sMhHWo3IBD3AKjGdSlpyNApDwYTaGvenMe+xtUWSSxYIy2ptWAWfkpOtUMx3lI3brZJRt8s98xS5m972SC9mlNT2ZU+i6hZ6srYv2w4nDuyX+j7Q6IGqvYtabxUWzza/Zg0yNpPScvvzscW1CVhdEd5qYH6OKfBfuVOj3ZG4pCycvbejhkUJwbCQ5m8+DEXUol8BKeh92+TPC3jDHXWIStdgLIrmkZ3YWxMQBgQ41QIkaf6X1/0WYEcY0DFW6hlDzg2GbJ8tPRRPC9dfgMs3ZMKJkc7e4x7wMvG2QXQ0aO6e7xTMw41JZ/OrIit0JDHB1M8bWDPUhHwjiCht4W77n7KWFk9sIUDzOdMdd69BIMt5IohtkjnIT2dXekB4xiNvfPLYUa70aOuSHWi3HXVSE1R7RX0brmNf/mH1Pm91uun3UqtIwhrpD0gteQnc0EAlHoOJOazdn3cohrtmECZJo+f+EiqFfEHT2hBrHPEvWknNfxAyPS7jYWKQ7pTMk+y/BUkLyIQkimvNz41azA6sA75nnQrZ+ZJQa+KP2cEObMBs/ekzA45nds1UXpolI1W8QIOxJ/Y10C1yxr6V5a3WWg1H8EbF0HaqiyIeQx/UCz7gl62CbLEDui9PA=
file_glob: true
file: ".pioenvs/*.bin"
skip_cleanup: true
on:
tags: true

View File

@ -15,7 +15,7 @@ If you like **Sonoff-Tasmota**, give it a star, or fork it and contribute!
### Development
[![Build Status](https://img.shields.io/travis/arendst/Sonoff-Tasmota.svg)](https://travis-ci.org/arendst/Sonoff-Tasmota)
Current version is **6.1.1b** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for release information and [sonoff/_changelog.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_changelog.ino) for change information.
Current version is **6.1.1c** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for release information and [sonoff/_changelog.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_changelog.ino) for change information.
### Disclaimer
:warning: **DANGER OF ELECTROCUTION** :warning:

View File

@ -0,0 +1,27 @@
language: c
sudo: false
# Blacklist
branches:
except:
- gh-pages
env:
global:
- PRETTYNAME="Adafruit CCS811 Arduino Library"
# Optional, will default to "$TRAVIS_BUILD_DIR/Doxyfile"
# - DOXYFILE: $TRAVIS_BUILD_DIR/Doxyfile
before_install:
- source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/install.sh)
install:
- arduino --install-library "Adafruit SSD1306","Adafruit GFX Library"
script:
- build_main_platforms
# Generate and deploy documentation
after_success:
- source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/library_check.sh)
- source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/doxy_gen_and_deploy.sh)

View File

@ -0,0 +1,280 @@
#include "Adafruit_CCS811.h"
/**************************************************************************/
/*!
@brief Setups the I2C interface and hardware and checks for communication.
@param addr Optional I2C address the sensor can be found on. Default is 0x5A
@returns True if device is set up, false on any failure
*/
/**************************************************************************/
sint8_t Adafruit_CCS811::begin(uint8_t addr)
{
_i2caddr = addr;
_i2c_init();
SWReset();
delay(100);
//check that the HW id is correct
if(this->read8(CCS811_HW_ID) != CCS811_HW_ID_CODE) {
return -1;
}
//try to start the app
this->write(CCS811_BOOTLOADER_APP_START, NULL, 0);
delay(100);
//make sure there are no errors and we have entered application mode
if(checkError()) {
return -2;
}
if(!_status.FW_MODE) {
return -3;
}
disableInterrupt();
//default to read every second
setDriveMode(CCS811_DRIVE_MODE_1SEC);
return 0;
}
/**************************************************************************/
/*!
@brief sample rate of the sensor.
@param mode one of CCS811_DRIVE_MODE_IDLE, CCS811_DRIVE_MODE_1SEC, CCS811_DRIVE_MODE_10SEC, CCS811_DRIVE_MODE_60SEC, CCS811_DRIVE_MODE_250MS.
*/
void Adafruit_CCS811::setDriveMode(uint8_t mode)
{
_meas_mode.DRIVE_MODE = mode;
this->write8(CCS811_MEAS_MODE, _meas_mode.get());
}
/**************************************************************************/
/*!
@brief enable the data ready interrupt pin on the device.
*/
/**************************************************************************/
void Adafruit_CCS811::enableInterrupt()
{
_meas_mode.INT_DATARDY = 1;
this->write8(CCS811_MEAS_MODE, _meas_mode.get());
}
/**************************************************************************/
/*!
@brief disable the data ready interrupt pin on the device
*/
/**************************************************************************/
void Adafruit_CCS811::disableInterrupt()
{
_meas_mode.INT_DATARDY = 0;
this->write8(CCS811_MEAS_MODE, _meas_mode.get());
}
/**************************************************************************/
/*!
@brief checks if data is available to be read.
@returns True if data is ready, false otherwise.
*/
/**************************************************************************/
bool Adafruit_CCS811::available()
{
_status.set(read8(CCS811_STATUS));
if(!_status.DATA_READY)
return false;
else return true;
}
/**************************************************************************/
/*!
@brief read and store the sensor data. This data can be accessed with getTVOC() and geteCO2()
@returns 0 if no error, error code otherwise.
*/
/**************************************************************************/
uint8_t Adafruit_CCS811::readData()
{
if(!available())
return false;
else{
uint8_t buf[8];
this->read(CCS811_ALG_RESULT_DATA, buf, 8);
_eCO2 = ((uint16_t)buf[0] << 8) | ((uint16_t)buf[1]);
_TVOC = ((uint16_t)buf[2] << 8) | ((uint16_t)buf[3]);
if(_status.ERROR)
return buf[5];
else return 0;
}
}
/**************************************************************************/
/*!
@brief set the humidity and temperature compensation for the sensor.
@param humidity the humidity data as a percentage. For 55% humidity, pass in integer 55.
@param temperature the temperature in degrees C as a decimal number. For 25.5 degrees C, pass in 25.5
*/
/**************************************************************************/
void Adafruit_CCS811::setEnvironmentalData(uint8_t humidity, double temperature)
{
/* Humidity is stored as an unsigned 16 bits in 1/512%RH. The
default value is 50% = 0x64, 0x00. As an example 48.5%
humidity would be 0x61, 0x00.*/
/* Temperature is stored as an unsigned 16 bits integer in 1/512
degrees; there is an offset: 0 maps to -25°C. The default value is
25°C = 0x64, 0x00. As an example 23.5% temperature would be
0x61, 0x00.
The internal algorithm uses these values (or default values if
not set by the application) to compensate for changes in
relative humidity and ambient temperature.*/
uint8_t hum_perc = humidity << 1;
float fractional = modf(temperature, &temperature);
uint16_t temp_high = (((uint16_t)temperature + 25) << 9);
uint16_t temp_low = ((uint16_t)(fractional / 0.001953125) & 0x1FF);
uint16_t temp_conv = (temp_high | temp_low);
uint8_t buf[] = {hum_perc, 0x00,
(uint8_t)((temp_conv >> 8) & 0xFF), (uint8_t)(temp_conv & 0xFF)};
this->write(CCS811_ENV_DATA, buf, 4);
}
/**************************************************************************/
/*!
@brief calculate the temperature using the onboard NTC resistor.
@returns temperature as a double.
*/
/**************************************************************************/
double Adafruit_CCS811::calculateTemperature()
{
uint8_t buf[4];
this->read(CCS811_NTC, buf, 4);
uint32_t vref = ((uint32_t)buf[0] << 8) | buf[1];
uint32_t vntc = ((uint32_t)buf[2] << 8) | buf[3];
//from ams ccs811 app note
uint32_t rntc = vntc * CCS811_REF_RESISTOR / vref;
double ntc_temp;
ntc_temp = log((double)rntc / CCS811_REF_RESISTOR); // 1
ntc_temp /= 3380; // 2
ntc_temp += 1.0 / (25 + 273.15); // 3
ntc_temp = 1.0 / ntc_temp; // 4
ntc_temp -= 273.15; // 5
return ntc_temp - _tempOffset;
}
/**************************************************************************/
/*!
@brief set interrupt thresholds
@param low_med the level below which an interrupt will be triggered.
@param med_high the level above which the interrupt will ge triggered.
@param hysteresis optional histeresis level. Defaults to 50
*/
/**************************************************************************/
void Adafruit_CCS811::setThresholds(uint16_t low_med, uint16_t med_high, uint8_t hysteresis)
{
uint8_t buf[] = {(uint8_t)((low_med >> 8) & 0xF), (uint8_t)(low_med & 0xF),
(uint8_t)((med_high >> 8) & 0xF), (uint8_t)(med_high & 0xF), hysteresis};
this->write(CCS811_THRESHOLDS, buf, 5);
}
/**************************************************************************/
/*!
@brief trigger a software reset of the device
*/
/**************************************************************************/
void Adafruit_CCS811::SWReset()
{
//reset sequence from the datasheet
uint8_t seq[] = {0x11, 0xE5, 0x72, 0x8A};
this->write(CCS811_SW_RESET, seq, 4);
}
/**************************************************************************/
/*!
@brief read the status register and store any errors.
@returns the error bits from the status register of the device.
*/
/**************************************************************************/
bool Adafruit_CCS811::checkError()
{
_status.set(read8(CCS811_STATUS));
return _status.ERROR;
}
/**************************************************************************/
/*!
@brief write one byte of data to the specified register
@param reg the register to write to
@param value the value to write
*/
/**************************************************************************/
void Adafruit_CCS811::write8(byte reg, byte value)
{
this->write(reg, &value, 1);
}
/**************************************************************************/
/*!
@brief read one byte of data from the specified register
@param reg the register to read
@returns one byte of register data
*/
/**************************************************************************/
uint8_t Adafruit_CCS811::read8(byte reg)
{
uint8_t ret;
this->read(reg, &ret, 1);
return ret;
}
void Adafruit_CCS811::_i2c_init()
{
Wire.begin();
#ifdef ESP8266
Wire.setClockStretchLimit(1000);
#endif
}
void Adafruit_CCS811::read(uint8_t reg, uint8_t *buf, uint8_t num)
{
uint8_t value;
uint8_t pos = 0;
//on arduino we need to read in 32 byte chunks
while(pos < num){
uint8_t read_now = min((uint8_t)32, (uint8_t)(num - pos));
Wire.beginTransmission((uint8_t)_i2caddr);
Wire.write((uint8_t)reg + pos);
Wire.endTransmission();
Wire.requestFrom((uint8_t)_i2caddr, read_now);
for(int i=0; i<read_now; i++){
buf[pos] = Wire.read();
pos++;
}
}
}
void Adafruit_CCS811::write(uint8_t reg, uint8_t *buf, uint8_t num)
{
Wire.beginTransmission((uint8_t)_i2caddr);
Wire.write((uint8_t)reg);
Wire.write((uint8_t *)buf, num);
Wire.endTransmission();
}

View File

@ -0,0 +1,231 @@
#ifndef LIB_ADAFRUIT_CCS811_H
#define LIB_ADAFRUIT_CCS811_H
#if (ARDUINO >= 100)
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <Wire.h>
/*=========================================================================
I2C ADDRESS/BITS
-----------------------------------------------------------------------*/
#define CCS811_ADDRESS (0x5A)
/*=========================================================================*/
/*=========================================================================
REGISTERS
-----------------------------------------------------------------------*/
enum
{
CCS811_STATUS = 0x00,
CCS811_MEAS_MODE = 0x01,
CCS811_ALG_RESULT_DATA = 0x02,
CCS811_RAW_DATA = 0x03,
CCS811_ENV_DATA = 0x05,
CCS811_NTC = 0x06,
CCS811_THRESHOLDS = 0x10,
CCS811_BASELINE = 0x11,
CCS811_HW_ID = 0x20,
CCS811_HW_VERSION = 0x21,
CCS811_FW_BOOT_VERSION = 0x23,
CCS811_FW_APP_VERSION = 0x24,
CCS811_ERROR_ID = 0xE0,
CCS811_SW_RESET = 0xFF,
};
//bootloader registers
enum
{
CCS811_BOOTLOADER_APP_ERASE = 0xF1,
CCS811_BOOTLOADER_APP_DATA = 0xF2,
CCS811_BOOTLOADER_APP_VERIFY = 0xF3,
CCS811_BOOTLOADER_APP_START = 0xF4
};
enum
{
CCS811_DRIVE_MODE_IDLE = 0x00,
CCS811_DRIVE_MODE_1SEC = 0x01,
CCS811_DRIVE_MODE_10SEC = 0x02,
CCS811_DRIVE_MODE_60SEC = 0x03,
CCS811_DRIVE_MODE_250MS = 0x04,
};
/*=========================================================================*/
#define CCS811_HW_ID_CODE 0x81
#define CCS811_REF_RESISTOR 100000
/**************************************************************************/
/*!
@brief Class that stores state and functions for interacting with CCS811 gas sensor chips
*/
/**************************************************************************/
class Adafruit_CCS811 {
public:
//constructors
Adafruit_CCS811(void) {};
~Adafruit_CCS811(void) {};
sint8_t begin(uint8_t addr = CCS811_ADDRESS);
void setEnvironmentalData(uint8_t humidity, double temperature);
//calculate temperature based on the NTC register
double calculateTemperature();
void setThresholds(uint16_t low_med, uint16_t med_high, uint8_t hysteresis = 50);
void SWReset();
void setDriveMode(uint8_t mode);
void enableInterrupt();
void disableInterrupt();
/**************************************************************************/
/*!
@brief returns the stored total volatile organic compounds measurement. This does does not read the sensor. To do so, call readData()
@returns TVOC measurement as 16 bit integer
*/
/**************************************************************************/
uint16_t getTVOC() { return _TVOC; }
/**************************************************************************/
/*!
@brief returns the stored estimated carbon dioxide measurement. This does does not read the sensor. To do so, call readData()
@returns eCO2 measurement as 16 bit integer
*/
/**************************************************************************/
uint16_t geteCO2() { return _eCO2; }
/**************************************************************************/
/*!
@brief set the temperature compensation offset for the device. This is needed to offset errors in NTC measurements.
@param offset the offset to be added to temperature measurements.
*/
/**************************************************************************/
void setTempOffset(float offset) { _tempOffset = offset; }
//check if data is available to be read
bool available();
uint8_t readData();
bool checkError();
private:
uint8_t _i2caddr;
float _tempOffset;
uint16_t _TVOC;
uint16_t _eCO2;
void write8(byte reg, byte value);
void write16(byte reg, uint16_t value);
uint8_t read8(byte reg);
void read(uint8_t reg, uint8_t *buf, uint8_t num);
void write(uint8_t reg, uint8_t *buf, uint8_t num);
void _i2c_init();
/*=========================================================================
REGISTER BITFIELDS
-----------------------------------------------------------------------*/
// The status register
struct status {
/* 0: no error
* 1: error has occurred
*/
uint8_t ERROR: 1;
// reserved : 2
/* 0: no samples are ready
* 1: samples are ready
*/
uint8_t DATA_READY: 1;
uint8_t APP_VALID: 1;
// reserved : 2
/* 0: boot mode, new firmware can be loaded
* 1: application mode, can take measurements
*/
uint8_t FW_MODE: 1;
void set(uint8_t data){
ERROR = data & 0x01;
DATA_READY = (data >> 3) & 0x01;
APP_VALID = (data >> 4) & 0x01;
FW_MODE = (data >> 7) & 0x01;
}
};
status _status;
//measurement and conditions register
struct meas_mode {
// reserved : 2
/* 0: interrupt mode operates normally
* 1: Interrupt mode (if enabled) only asserts the nINT signal (driven low) if the new
ALG_RESULT_DATA crosses one of the thresholds set in the THRESHOLDS register
by more than the hysteresis value (also in the THRESHOLDS register)
*/
uint8_t INT_THRESH: 1;
/* 0: int disabled
* 1: The nINT signal is asserted (driven low) when a new sample is ready in
ALG_RESULT_DATA. The nINT signal will stop being driven low when
ALG_RESULT_DATA is read on the I²C interface.
*/
uint8_t INT_DATARDY: 1;
uint8_t DRIVE_MODE: 3;
uint8_t get(){
return (INT_THRESH << 2) | (INT_DATARDY << 3) | (DRIVE_MODE << 4);
}
};
meas_mode _meas_mode;
struct error_id {
/* The CCS811 received an I²C write request addressed to this station but with
invalid register address ID */
uint8_t WRITE_REG_INVALID: 1;
/* The CCS811 received an I²C read request to a mailbox ID that is invalid */
uint8_t READ_REG_INVALID: 1;
/* The CCS811 received an I²C request to write an unsupported mode to
MEAS_MODE */
uint8_t MEASMODE_INVALID: 1;
/* The sensor resistance measurement has reached or exceeded the maximum
range */
uint8_t MAX_RESISTANCE: 1;
/* The Heater current in the CCS811 is not in range */
uint8_t HEATER_FAULT: 1;
/* The Heater voltage is not being applied correctly */
uint8_t HEATER_SUPPLY: 1;
void set(uint8_t data){
WRITE_REG_INVALID = data & 0x01;
READ_REG_INVALID = (data & 0x02) >> 1;
MEASMODE_INVALID = (data & 0x04) >> 2;
MAX_RESISTANCE = (data & 0x08) >> 3;
HEATER_FAULT = (data & 0x10) >> 4;
HEATER_SUPPLY = (data & 0x20) >> 5;
}
};
error_id _error_id;
/*=========================================================================*/
};
#endif

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017 Adafruit Industries
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,13 @@
# Adafruit CCS811 Library [![Build Status](https://travis-ci.org/adafruit/Adafruit_CCS811.svg?branch=master)](https://travis-ci.org/adafruit/Adafruit_CCS811)
<img src="https://cdn-shop.adafruit.com/970x728/3566-00.jpg" height="300"/>
This is a library for the Adafruit CCS811 gas sensor breakout board:
* https://www.adafruit.com/product/3566
Check out the links above for our tutorials and wiring diagrams. This chip uses I2C to communicate
Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
Written by Dean Miller for Adafruit Industries.
MIT license, all text above must be included in any redistribution

View File

@ -0,0 +1,80 @@
/* This demo shows how to display the CCS811 readings on an Adafruit I2C OLED.
* (We used a Feather + OLED FeatherWing)
*/
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "Adafruit_CCS811.h"
Adafruit_CCS811 ccs;
Adafruit_SSD1306 display = Adafruit_SSD1306();
void setup() {
Serial.begin(115200);
if(!ccs.begin()){
Serial.println("Failed to start sensor! Please check your wiring.");
while(1);
}
// by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128x32)
// Show image buffer on the display hardware.
// Since the buffer is intialized with an Adafruit splashscreen
// internally, this will display the splashscreen.
display.display();
delay(500);
// Clear the buffer.
display.clearDisplay();
display.display();
//calibrate temperature sensor
while(!ccs.available());
float temp = ccs.calculateTemperature();
ccs.setTempOffset(temp - 25.0);
Serial.println("IO test");
// text display tests
display.setTextSize(1);
display.setTextColor(WHITE);
}
void loop() {
display.setCursor(0,0);
if(ccs.available()){
display.clearDisplay();
float temp = ccs.calculateTemperature();
if(!ccs.readData()){
display.print("eCO2: ");
Serial.print("eCO2: ");
float eCO2 = ccs.geteCO2();
display.print(eCO2);
Serial.print(eCO2);
display.print(" ppm\nTVOC: ");
Serial.print(" ppm, TVOC: ");
float TVOC = ccs.getTVOC();
display.print(TVOC);
Serial.print(TVOC);
Serial.print(" ppb Temp:");
display.print(" ppb\nTemp: ");
Serial.println(temp);
display.println(temp);
display.display();
}
else{
Serial.println("ERROR!");
while(1);
}
}
delay(500);
}

View File

@ -0,0 +1,56 @@
/***************************************************************************
This is a library for the CCS811 air
This sketch reads the sensor
Designed specifically to work with the Adafruit CCS811 breakout
----> http://www.adafruit.com/products/3566
These sensors use I2C to communicate. The device's I2C address is 0x5A
Adafruit invests time and resources providing this open source code,
please support Adafruit andopen-source hardware by purchasing products
from Adafruit!
Written by Dean Miller for Adafruit Industries.
BSD license, all text above must be included in any redistribution
***************************************************************************/
#include "Adafruit_CCS811.h"
Adafruit_CCS811 ccs;
void setup() {
Serial.begin(9600);
Serial.println("CCS811 test");
if(!ccs.begin()){
Serial.println("Failed to start sensor! Please check your wiring.");
while(1);
}
//calibrate temperature sensor
while(!ccs.available());
float temp = ccs.calculateTemperature();
ccs.setTempOffset(temp - 25.0);
}
void loop() {
if(ccs.available()){
float temp = ccs.calculateTemperature();
if(!ccs.readData()){
Serial.print("CO2: ");
Serial.print(ccs.geteCO2());
Serial.print("ppm, TVOC: ");
Serial.print(ccs.getTVOC());
Serial.print("ppb Temp:");
Serial.println(temp);
}
else{
Serial.println("ERROR!");
while(1);
}
}
delay(500);
}

View File

@ -0,0 +1,9 @@
name=Adafruit CCS811 Library
version=1.0.0
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=This is a library for the Adafruit CCS811 I2C gas sensor breakout.
paragraph=This is a library for the Adafruit CCS811 I2C gas sensor breakou.
category=Sensors
url=https://github.com/adafruit/Adafruit_CCS811
architectures=*

View File

@ -1,14 +1,14 @@
# datatypes
address_t DATA_TYPE
message_t DATA_TYPE
callback_id_t DATA_TYPE
callback_assignment_id_t DATA_TYPE
option_entry_t DATA_TYPE
config_id_t DATA_TYPE
enable_condition_t DATA_TYPE
callback_fptr_t DATA_TYPE
feedback_action_fptr_t DATA_TYPE
knx_command_type_t DATA_TYPE
address_t KEYWORD1 DATA_TYPE
message_t KEYWORD1 DATA_TYPE
callback_id_t KEYWORD1 DATA_TYPE
callback_assignment_id_t KEYWORD1 DATA_TYPE
option_entry_t KEYWORD1 DATA_TYPE
config_id_t KEYWORD1 DATA_TYPE
enable_condition_t KEYWORD1 DATA_TYPE
callback_fptr_t KEYWORD1 DATA_TYPE
feedback_action_fptr_t KEYWORD1 DATA_TYPE
knx_command_type_t KEYWORD1 DATA_TYPE
# methods
setup KEYWORD2
@ -92,13 +92,13 @@ answer_4byte_int KEYWORD2
answer_4byte_uint KEYWORD2
answer_4byte_float KEYWORD2
data_to_1byte_int KEYWORD 2
data_to_2byte_int KEYWORD 2
data_to_2byte_float KEYWORD 2
data_to_4byte_float KEYWORD 2
data_to_3byte_color KEYWORD 2
data_to_3byte_time KEYWORD 2
data_to_3byte_data KEYWORD 2
data_to_1byte_int KEYWORD2
data_to_2byte_int KEYWORD2
data_to_2byte_float KEYWORD2
data_to_4byte_float KEYWORD2
data_to_3byte_color KEYWORD2
data_to_3byte_time KEYWORD2
data_to_3byte_data KEYWORD2
# constants
knx LITERAL1
knx LITERAL1

View File

@ -1,4 +1,12 @@
/* 6.1.1b
/* 6.1.1c
* Add support for CCS811 sensor (#3309)
* Add command Timers 0/1 to globally disable or enable armed timers (#3270)
*
* 6.1.1b
* Add support for MPR121 controller in input mode for touch buttons (#3142)
* Add support for MCP230xx for general purpose input expansion and command Sensor29 (#3188)
* Fix command Scale buffer overflow (#3236)
* Fix rules once regression from v6.1.0 (#3198, #3226)
* Add default Wifi Configuration tool as define WIFI_CONFIG_NO_SSID in user_config.h if no SSID is configured (#3224)
* Add user selection of Wifi Smartconfig as define USE_SMARTCONFIG in user_config.h
* Add user selection of WPS as define USE_WPS in user_config.h in preparation for core v2.4.2 (#3221)

View File

@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "Подтвърдете рестартирането"
#define D_CONFIGURE_MODULE "Конфигурация на модула"
#define D_CONFIGURE_MCP230XX "Конфигурация на MCP230xx"
#define D_CONFIGURE_WIFI "Конфигурация на WiFi"
#define D_CONFIGURE_MQTT "Конфигурация на MQTT"
#define D_CONFIGURE_DOMOTICZ "Конфигурация на Domoticz"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Конфигуриране на таймер"
#define D_TIMER_PARAMETERS "Параметри на таймера"
#define D_TIMER_ENABLE "Активиране на таймера"
#define D_TIMER_ARM "Arm"
#define D_TIMER_TIME "Време"
#define D_TIMER_DAYS "Дни"

View File

@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "Potvrzení restartu"
#define D_CONFIGURE_MODULE "Nastavení modulu"
#define D_CONFIGURE_MCP230XX "Nastavení MCP230xx"
#define D_CONFIGURE_WIFI "Nastavení WiFi"
#define D_CONFIGURE_MQTT "Nastavení MQTT"
#define D_CONFIGURE_DOMOTICZ "Nastavení Domoticz"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Nastavení Časovače"
#define D_TIMER_PARAMETERS "Časovač"
#define D_TIMER_ENABLE "Povol Časovače"
#define D_TIMER_ARM "Aktivní"
#define D_TIMER_TIME "Čas"
#define D_TIMER_DAYS "Dny"

View File

@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "Wirklich neustarten?"
#define D_CONFIGURE_MODULE "Gerät konfigurieren"
#define D_CONFIGURE_MCP230XX "MCP230xx konfigurieren"
#define D_CONFIGURE_WIFI "WLAN konfigurieren"
#define D_CONFIGURE_MQTT "MQTT konfigurieren"
#define D_CONFIGURE_DOMOTICZ "Domoticz konfigurieren"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Zeitplan konfigurieren"
#define D_TIMER_PARAMETERS "Zeitplan-Einstellungen"
#define D_TIMER_ENABLE "Zeitpläne aktivieren"
#define D_TIMER_ARM "Aktiv"
#define D_TIMER_TIME "Uhrzeit"
#define D_TIMER_DAYS "Wochentage"

View File

@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "Επιβεβαίωση Επανεκκίνησης"
#define D_CONFIGURE_MODULE "Ρύθμιση Module"
#define D_CONFIGURE_MCP230XX "Ρύθμιση MCP230xx"
#define D_CONFIGURE_WIFI "Ρύθμιση WiFi"
#define D_CONFIGURE_MQTT "Ρύθμιση MQTT"
#define D_CONFIGURE_DOMOTICZ "Ρύθμιση Domoticz"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Ρυθμίσεις Χρόνου"
#define D_TIMER_PARAMETERS "Χρονικοί παράμετροι"
#define D_TIMER_ENABLE "Ενεργοποιημένο Χρονικοί"
#define D_TIMER_ARM "Arm"
#define D_TIMER_TIME "Ωρα"
#define D_TIMER_DAYS "Μέρες"

View File

@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "Confirm Restart"
#define D_CONFIGURE_MODULE "Configure Module"
#define D_CONFIGURE_MCP230XX "Configure MCP230xx"
#define D_CONFIGURE_WIFI "Configure WiFi"
#define D_CONFIGURE_MQTT "Configure MQTT"
#define D_CONFIGURE_DOMOTICZ "Configure Domoticz"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Configure Timer"
#define D_TIMER_PARAMETERS "Timer parameters"
#define D_TIMER_ENABLE "Enable Timers"
#define D_TIMER_ARM "Arm"
#define D_TIMER_TIME "Time"
#define D_TIMER_DAYS "Days"

View File

@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "Confirmar Reinicio"
#define D_CONFIGURE_MODULE "Configuración del Módulo"
#define D_CONFIGURE_MCP230XX "Configuración MCP230xx"
#define D_CONFIGURE_WIFI "Configuración WiFi"
#define D_CONFIGURE_MQTT "Configuración MQTT"
#define D_CONFIGURE_DOMOTICZ "Configuración Domoticz"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Configuración Temporizadores"
#define D_TIMER_PARAMETERS "Parámetros de Temporizadores"
#define D_TIMER_ENABLE "Habilitar Temporizadores"
#define D_TIMER_ARM "Activo"
#define D_TIMER_TIME "Hora"
#define D_TIMER_DAYS "Días"

View File

@ -28,7 +28,7 @@
* Use online command StateText to translate ON, OFF, HOLD and TOGGLE.
* Use online command Prefix to translate cmnd, stat and tele.
*
* Updated until v5.14.0a
* Updated until v6.1.1b
\*********************************************************************/
#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English)
@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "Confirmer redémarrage"
#define D_CONFIGURE_MODULE "Configuration du Module"
#define D_CONFIGURE_MCP230XX "Configuration MCP230xx"
#define D_CONFIGURE_WIFI "Configuration WiFi"
#define D_CONFIGURE_MQTT "Configuration MQTT"
#define D_CONFIGURE_DOMOTICZ "Configuration Domoticz"
@ -241,8 +242,8 @@
#define D_MODULE_PARAMETERS "Paramètres module"
#define D_MODULE_TYPE "Type de module"
#define D_GPIO "GPIO"
#define D_SERIAL_IN "Serial In"
#define D_SERIAL_OUT "Serial Out"
#define D_SERIAL_IN "Entrée série"
#define D_SERIAL_OUT "Sortie série"
#define D_WIFI_PARAMETERS "Paramètres Wifi"
#define D_SCAN_FOR_WIFI_NETWORKS "Scan des réseaux wifi"
@ -321,10 +322,10 @@
#define D_UPLOAD_ERR_7 "Téléchargement annulé"
#define D_UPLOAD_ERR_8 "Fichier invalide"
#define D_UPLOAD_ERR_9 "Fichier trop grand"
#define D_UPLOAD_ERR_10 "Failed to init RF chip"
#define D_UPLOAD_ERR_11 "Failed to erase RF chip"
#define D_UPLOAD_ERR_12 "Failed to write to RF chip"
#define D_UPLOAD_ERR_13 "Failed to decode RF firmware"
#define D_UPLOAD_ERR_10 "Erreur d'initialisation du chip RF"
#define D_UPLOAD_ERR_11 "Erreur d'effacement du chip RF"
#define D_UPLOAD_ERR_12 "Erreur d'accès en écriture au chip RF"
#define D_UPLOAD_ERR_13 "Erreur de décodage du firmware RF"
#define D_UPLOAD_ERROR_CODE "Code d'erreur téléchargement"
#define D_ENTER_COMMAND "Saisir une commande"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Configuration des Timers"
#define D_TIMER_PARAMETERS "Paramètres Timer"
#define D_TIMER_ENABLE "Activer des Timers"
#define D_TIMER_ARM "Armer"
#define D_TIMER_TIME "Temps"
#define D_TIMER_DAYS "Jours"
@ -405,7 +407,7 @@
#define D_KNX_COMMAND_OTHER "Autre"
#define D_SENT_TO "envoyé à"
#define D_KNX_WARNING "L'Adresse de Groupe ( 0 / 0 / 0 ) est réservée et ne peut être utilisée."
#define D_KNX_ENHANCEMENT "Communication Enhancement"
#define D_KNX_ENHANCEMENT "Amélioration de la communication"
#define D_KNX_TX_SLOT "KNX TX"
#define D_KNX_RX_SLOT "KNX RX"

View File

@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "Újraindítás megerősítése"
#define D_CONFIGURE_MODULE "Eszköz konfiguráció"
#define D_CONFIGURE_MCP230XX "MCP230xx konfiguráció"
#define D_CONFIGURE_WIFI "WiFi konfiguráció"
#define D_CONFIGURE_MQTT "MQTT konfiguráció"
#define D_CONFIGURE_DOMOTICZ "Domoticz konfiguráció"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Configure Timer"
#define D_TIMER_PARAMETERS "Timer parameters"
#define D_TIMER_ENABLE "Enable Timers"
#define D_TIMER_ARM "Arm"
#define D_TIMER_TIME "Time"
#define D_TIMER_DAYS "Days"

View File

@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "Conferma Riavvio"
#define D_CONFIGURE_MODULE "Configurazione Modulo"
#define D_CONFIGURE_MCP230XX "Configurazione MCP230xx"
#define D_CONFIGURE_WIFI "Configurazione WiFi"
#define D_CONFIGURE_MQTT "Configurazione MQTT"
#define D_CONFIGURE_DOMOTICZ "Configurazione Domoticz"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Configura Timer"
#define D_TIMER_PARAMETERS "Parametri Timer"
#define D_TIMER_ENABLE "Abilita Timers"
#define D_TIMER_ARM "Attiva"
#define D_TIMER_TIME "Ora"
#define D_TIMER_DAYS "Giorni"

View File

@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "Bevestig herstart"
#define D_CONFIGURE_MODULE "Configureer Module"
#define D_CONFIGURE_MCP230XX "Configureer MCP230xx"
#define D_CONFIGURE_WIFI "Configureer WiFi"
#define D_CONFIGURE_MQTT "Configureer MQTT"
#define D_CONFIGURE_DOMOTICZ "Configureer Domoticz"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Configureer Tijdschakelaar"
#define D_TIMER_PARAMETERS "Tijdschakelaar parameters"
#define D_TIMER_ENABLE "Tijdschakelaars inschakelen"
#define D_TIMER_ARM "Actief"
#define D_TIMER_TIME "Tijd"
#define D_TIMER_DAYS "Dagen"

View File

@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "Potwierdź restart"
#define D_CONFIGURE_MODULE "Konfiguruj moduł"
#define D_CONFIGURE_MCP230XX "Konfiguruj MCP230xx"
#define D_CONFIGURE_WIFI "Konfiguruj WiFi"
#define D_CONFIGURE_MQTT "Konfiguruj MQTT"
#define D_CONFIGURE_DOMOTICZ "Konfiguruj Domoticz"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Configure Timer"
#define D_TIMER_PARAMETERS "Timer parameters"
#define D_TIMER_ENABLE "Enable Timers"
#define D_TIMER_ARM "Arm"
#define D_TIMER_TIME "Time"
#define D_TIMER_DAYS "Days"

View File

@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "Confirmar o reinicio"
#define D_CONFIGURE_MODULE "Configurar Módulo"
#define D_CONFIGURE_MCP230XX "Configurar MCP230xx"
#define D_CONFIGURE_WIFI "Configurar WiFi"
#define D_CONFIGURE_MQTT "Configurar MQTT"
#define D_CONFIGURE_DOMOTICZ "Configurar Domoticz"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Configurar temporizador"
#define D_TIMER_PARAMETERS "Parâmetros"
#define D_TIMER_ENABLE "Habilitar temporizadores"
#define D_TIMER_ARM "Habilitar"
#define D_TIMER_TIME "Horário"
#define D_TIMER_DAYS "Dias"

View File

@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "Confirmar o reinicio"
#define D_CONFIGURE_MODULE "Configurar Módulo"
#define D_CONFIGURE_MCP230XX "Configurar MCP230xx"
#define D_CONFIGURE_WIFI "Configurar WiFi"
#define D_CONFIGURE_MQTT "Configurar MQTT"
#define D_CONFIGURE_DOMOTICZ "Configurar Domoticz"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Configure Timer"
#define D_TIMER_PARAMETERS "Timer parameters"
#define D_TIMER_ENABLE "Enable Timers"
#define D_TIMER_ARM "Arm"
#define D_TIMER_TIME "Time"
#define D_TIMER_DAYS "Days"

View File

@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "Подтвердить перезагрузку"
#define D_CONFIGURE_MODULE "Конфигурация Модуля"
#define D_CONFIGURE_MCP230XX "Конфигурация MCP230xx"
#define D_CONFIGURE_WIFI "Конфигурация WiFi"
#define D_CONFIGURE_MQTT "Конфигурация MQTT"
#define D_CONFIGURE_DOMOTICZ "Конфигурация Domoticz"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Configure Timer"
#define D_TIMER_PARAMETERS "Timer parameters"
#define D_TIMER_ENABLE "Enable Timers"
#define D_TIMER_ARM "Arm"
#define D_TIMER_TIME "Time"
#define D_TIMER_DAYS "Days"

View File

@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "Підтвердити перезавантаження"
#define D_CONFIGURE_MODULE "Конфігурація модуля"
#define D_CONFIGURE_MCP230XX "Конфігурація MCP230xx"
#define D_CONFIGURE_WIFI "Конфігурація WiFi"
#define D_CONFIGURE_MQTT "Конфігурація MQTT"
#define D_CONFIGURE_DOMOTICZ "Конфігурація Domoticz"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Конфігурація таймеру"
#define D_TIMER_PARAMETERS "Налаштування таймеру"
#define D_TIMER_ENABLE "Увімкнений таймеру"
#define D_TIMER_ARM "Увімкнений"
#define D_TIMER_TIME "Час"
#define D_TIMER_DAYS "Дні"

View File

@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "确认重启"
#define D_CONFIGURE_MODULE "模块设置"
#define D_CONFIGURE_MCP230XX "MCP230xx设置"
#define D_CONFIGURE_WIFI "WiFi设置"
#define D_CONFIGURE_MQTT "MQTT设置"
#define D_CONFIGURE_DOMOTICZ "Domoticz设置"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "定时器设置"
#define D_TIMER_PARAMETERS "定时器参数"
#define D_TIMER_ENABLE "启用定时器"
#define D_TIMER_ARM "启用"
#define D_TIMER_TIME "时间"
#define D_TIMER_DAYS "天"

View File

@ -227,6 +227,7 @@
#define D_CONFIRM_RESTART "確認重啟"
#define D_CONFIGURE_MODULE "模塊設置"
#define D_CONFIGURE_MCP230XX "MCP230xx設置"
#define D_CONFIGURE_WIFI "WiFi設置"
#define D_CONFIGURE_MQTT "MQTT設置"
#define D_CONFIGURE_DOMOTICZ "Domoticz設置"
@ -379,6 +380,7 @@
// xdrv_09_timers.ino
#define D_CONFIGURE_TIMER "Configure Timer"
#define D_TIMER_PARAMETERS "Timer parameters"
#define D_TIMER_ENABLE "Enable Timers"
#define D_TIMER_ARM "Arm"
#define D_TIMER_TIME "Time"
#define D_TIMER_DAYS "Days"

View File

@ -63,7 +63,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
typedef union { // Restricted by MISRA-C Rule 18.4 but so usefull...
uint32_t data; // Allow bit manipulation using SetOption
struct { // SetOption50 .. SetOption81
uint32_t spare00 : 1;
uint32_t timers_enable : 1; // bit 0 (v6.1.1b)
uint32_t spare01 : 1;
uint32_t spare02 : 1;
uint32_t spare03 : 1;
@ -155,11 +155,9 @@ typedef union {
typedef union {
uint8_t data;
struct {
uint8_t enable : 1; // Enable INPUT
uint8_t pinmode : 3; // Enable INPUT
uint8_t pullup : 1; // Enable internal weak pull-up resistor
uint8_t inten : 1; // Enable Interrupt on PIN
uint8_t intmode : 1; // Change on STATE or match COMPARATOR
uint8_t intcomp : 1; // Interrupt COMPARATOR
uint8_t b4 : 1;
uint8_t b5 : 1;
uint8_t b6 : 1;
uint8_t b7 : 1;
@ -413,4 +411,4 @@ typedef union {
ADC_MODE(ADC_VCC); // Set ADC input for Power Supply Voltage usage
#endif
#endif // _SETTINGS_H_
#endif // _SETTINGS_H_

View File

@ -782,6 +782,9 @@ void SettingsDelta()
Settings.flag.rules_once = 0;
Settings.flag3.data = 0;
}
if (Settings.version < 0x06010103) {
Settings.flag3.timers_enable = 1;
}
Settings.version = VERSION;
SettingsSave(1);

5
sonoff/sonoff.ino Normal file → Executable file
View File

@ -25,7 +25,7 @@
- Select IDE Tools - Flash Size: "1M (no SPIFFS)"
====================================================*/
#define VERSION 0x06010102 // 6.1.1b
#define VERSION 0x06010103 // 6.1.1c
// Location specific includes
#include <core_version.h> // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0)
@ -188,6 +188,9 @@ uint8_t ntp_force_sync = 0; // Force NTP sync
StateBitfield global_state;
RulesBitfield rules_flag;
uint8_t glob_humidity = 0;
sint16_t glob_temperature = -9999;
char my_version[33]; // Composed version string
char my_hostname[33]; // Composed Wifi hostname
char mqtt_client[33]; // Composed MQTT Clientname

1
sonoff/sonoff_post.h Normal file → Executable file
View File

@ -80,6 +80,7 @@ void KNX_CB_Action(message_t const &msg, void *arg);
#define USE_INA219 // Add I2C code for INA219 Low voltage and current sensor (+1k code)
#define USE_MGS // Add I2C code for Xadow and Grove Mutichannel Gas sensor using library Multichannel_Gas_Sensor (+10k code)
//#define USE_APDS9960 // Add I2C code for APDS9960 Proximity Sensor. Disables SHT and VEML6070 (+4k7 code)
//#define USE_CCS811 // Add I2C code for CCS811 sensor (+2k2 code)
#define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code)
#define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code)
#ifndef CO2_LOW

View File

@ -130,6 +130,24 @@ size_t strchrspn(const char *str1, int character)
return ret;
}
// Function to return a substring defined by a delimiter at an index
char* subStr(char* dest, char* str, const char *delim, int index)
{
char *act;
char *sub;
char *ptr;
int i;
// Since strtok consumes the first arg, make a copy
strncpy(dest, str, strlen(str));
for (i = 1, act = dest; i <= index; i++, act = NULL) {
sub = strtok_r(act, delim, &ptr);
if (sub == NULL) break;
}
sub = Trim(sub);
return sub;
}
double CharToDouble(char *str)
{
// simple ascii to double, because atof or strtod are too large

View File

@ -237,7 +237,8 @@
//#define USE_MQTT_TLS // Use TLS for MQTT connection (+53k code, +15k mem)
// -- KNX IP Protocol -----------------------------
//#define USE_KNX // Enable KNX IP Protocol Support (+23k code, +3k3 mem)
//#define USE_KNX // Enable KNX IP Protocol Support (+9.4k code, +3k7 mem)
#define USE_KNX_WEB_MENU // Enable KNX WEB MENU (+8.3k code, +144 mem)
// -- HTTP ----------------------------------------
#define USE_WEBSERVER // Enable web server and Wifi Manager (+66k code, +8k mem)
@ -270,23 +271,26 @@
// -- I2C sensors ---------------------------------
#define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram)
#ifdef USE_I2C
#define USE_SHT // Add I2C emulating code for SHT1X sensor (+1k4 code)
#define USE_SHT3X // Add I2C code for SHT3x or SHTC3 sensor (+0k7 code)
#define USE_HTU // Add I2C code for HTU21/SI7013/SI7020/SI7021 sensor (+1k5 code)
#define USE_LM75AD // Add I2C code for LM75AD sensor (+0k5 code)
#define USE_BMP // Add I2C code for BMP085/BMP180/BMP280/BME280 sensor (+4k code)
// #define USE_BME680 // Add additional support for BME680 sensor using Bosch BME680 library (+4k code)
#define USE_SGP30 // Add I2C code for SGP30 sensor (+1k1 code)
#define USE_BH1750 // Add I2C code for BH1750 sensor (+0k5 code)
// #define USE_VEML6070 // Add I2C code for VEML6070 sensor (+0k5 code)
// #define USE_TSL2561 // Add I2C code for TSL2561 sensor using library Joba_Tsl2561 (+2k3 code)
// #define USE_SI1145 // Add I2C code for SI1145/46/47 sensor (+1k code)
// #define USE_ADS1115 // Add I2C code for ADS1115 16 bit A/D converter based on Adafruit ADS1x15 library (no library needed) (+0k7 code)
// #define USE_ADS1115_I2CDEV // Add I2C code for ADS1115 16 bit A/D converter using library i2cdevlib-Core and i2cdevlib-ADS1115 (+2k code)
// #define USE_INA219 // Add I2C code for INA219 Low voltage and current sensor (+1k code)
// #define USE_MGS // Add I2C code for Xadow and Grove Mutichannel Gas sensor using library Multichannel_Gas_Sensor (+10k code)
#define USE_SHT // Enable SHT1X sensor (+1k4 code)
#define USE_HTU // Enable HTU21/SI7013/SI7020/SI7021 sensor (I2C address 0x40) (+1k5 code)
#define USE_BMP // Enable BMP085/BMP180/BMP280/BME280 sensor (I2C address 0x76 or 0x77) (+4k code)
#define USE_BME680 // Enable support for BME680 sensor using Bosch BME680 library (+4k code)
#define USE_BH1750 // Enable BH1750 sensor (I2C address 0x23 or 0x5C) (+0k5 code)
// #define USE_VEML6070 // Enable VEML6070 sensor (I2C addresses 0x38 and 0x39) (+0k5 code)
// #define USE_ADS1115 // Enable ADS1115 16 bit A/D converter (I2C address 0x48, 0x49, 0x4A or 0x4B) based on Adafruit ADS1x15 library (no library needed) (+0k7 code)
// #define USE_ADS1115_I2CDEV // Enable ADS1115 16 bit A/D converter (I2C address 0x48, 0x49, 0x4A or 0x4B) using library i2cdevlib-Core and i2cdevlib-ADS1115 (+2k code)
// #define USE_INA219 // Enable INA219 (I2C address 0x40, 0x41 0x44 or 0x45) Low voltage and current sensor (+1k code)
#define USE_SHT3X // Enable SHT3x (I2C address 0x44 or 0x45) or SHTC3 (I2C address 0x70) sensor (+0k7 code)
// #define USE_TSL2561 // Enable TSL2561 sensor (I2C address 0x29, 0x39 or 0x49) using library Joba_Tsl2561 (+2k3 code)
// #define USE_MGS // Enable Xadow and Grove Mutichannel Gas sensor using library Multichannel_Gas_Sensor (+10k code)
#define MGS_SENSOR_ADDR 0x04 // Default Mutichannel Gas sensor i2c address
// #define USE_APDS9960 // Add I2C code for APDS9960 Proximity Sensor. Disables SHT and VEML6070 (+4k7 code)
#define USE_SGP30 // Enable SGP30 sensor (I2C address 0x58) (+1k1 code)
// #define USE_SI1145 // Enable SI1145/46/47 sensor (I2C address 0x60) (+1k code)
#define USE_LM75AD // Enable LM75AD sensor (I2C addresses 0x48 - 0x4F) (+0k5 code)
// #define USE_APDS9960 // Enable APDS9960 Proximity Sensor (I2C address 0x39). Disables SHT and VEML6070 (+4k7 code)
// #define USE_MCP230xx // Enable MCP23008/MCP23017 for GP INPUT ONLY (I2C addresses 0x20 - 0x27) providing command Sensor29 for configuration (+2k2 code)
// #define USE_MPR121 // Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code)
// #define USE_CCS811 // Enable CCS811 sensor (I2C address 0x5A) (+2k2 code)
#endif // USE_I2C
// -- SPI sensors ---------------------------------

View File

@ -204,7 +204,9 @@ const char HTTP_BTN_MENU_MQTT[] PROGMEM =
"";
const char HTTP_BTN_MENU4[] PROGMEM =
#ifdef USE_KNX
#ifdef USE_KNX_WEB_MENU
"<br/><form action='kn' method='get'><button>" D_CONFIGURE_KNX "</button></form>"
#endif // USE_KNX_WEB_MENU
#endif // USE_KNX
"<br/><form action='lg' method='get'><button>" D_CONFIGURE_LOGGING "</button></form>"
"<br/><form action='co' method='get'><button>" D_CONFIGURE_OTHER "</button></form>"
@ -391,7 +393,9 @@ void StartWebserver(int type, IPAddress ipweb)
#endif // USE_DOMOTICZ
}
#ifdef USE_KNX
#ifdef USE_KNX_WEB_MENU
WebServer->on("/kn", HandleKNXConfiguration);
#endif // USE_KNX_WEB_MENU
#endif // USE_KNX
WebServer->on("/lg", HandleLoggingConfiguration);
WebServer->on("/co", HandleOtherConfiguration);

View File

@ -262,7 +262,7 @@ void TimerEverySecond()
{
if (RtcTime.valid) {
if (!RtcTime.hour && !RtcTime.minute && !RtcTime.second) { TimerSetRandomWindows(); } // Midnight
if ((uptime > 60) && (RtcTime.minute != timer_last_minute)) { // Execute from one minute after restart every minute only once
if (Settings.flag3.timers_enable && (uptime > 60) && (RtcTime.minute != timer_last_minute)) { // Execute from one minute after restart every minute only once
timer_last_minute = RtcTime.minute;
int16_t time = (RtcTime.hour *60) + RtcTime.minute;
uint8_t days = 1 << (RtcTime.day_of_week -1);
@ -451,11 +451,15 @@ boolean TimerCommand()
}
}
else if (CMND_TIMERS == command_code) {
if (XdrvMailbox.data_len && (XdrvMailbox.payload == 0)) {
for (byte i = 0; i < MAX_TIMERS; i++) {
Settings.timer[i].arm = 0; // Disable all timers
if (XdrvMailbox.data_len) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) {
Settings.flag3.timers_enable = XdrvMailbox.payload;
}
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.flag3.timers_enable));
MqttPublishPrefixTopic_P(RESULT_OR_STAT, command);
byte jsflg = 0;
byte lines = 1;
for (byte i = 0; i < MAX_TIMERS; i++) {
@ -633,7 +637,9 @@ const char HTTP_TIMER_STYLE[] PROGMEM =
"</style>";
const char HTTP_FORM_TIMER[] PROGMEM =
"<fieldset style='min-width:470px;text-align:center;'><legend style='text-align:left;'><b>&nbsp;" D_TIMER_PARAMETERS "&nbsp;</b></legend><form method='post' action='sv'>"
"<input id='w' name='w' value='7,0' hidden><input id='t0' name='t0' value='";
"<input id='w' name='w' value='7,0' hidden>"
"<br/><input style='width:5%;' id='e0' name='e0' type='checkbox'{e0><b>" D_TIMER_ENABLE "</b><br/><br/><hr/>"
"<input id='t0' name='t0' value='";
const char HTTP_FORM_TIMER1[] PROGMEM =
"' hidden><div id='bt' name='bt'></div><br/><br/><br/>"
"<div id='oa' name='oa'></div><br/>"
@ -679,6 +685,7 @@ void HandleTimerConfiguration()
page += FPSTR(HTTP_HEAD_STYLE);
page.replace(F("</style>"), FPSTR(HTTP_TIMER_STYLE));
page += FPSTR(HTTP_FORM_TIMER);
page.replace(F("{e0"), (Settings.flag3.timers_enable) ? F(" checked") : F(""));
for (byte i = 0; i < MAX_TIMERS; i++) {
if (i > 0) { page += F(","); }
page += String(Settings.timer[i].data);
@ -702,9 +709,10 @@ void TimerSaveSettings()
char tmp[MAX_TIMERS *12]; // Need space for MAX_TIMERS x 10 digit numbers separated by a comma
Timer timer;
Settings.flag3.timers_enable = WebServer->hasArg("e0");
WebGetArg("t0", tmp, sizeof(tmp));
char *p = tmp;
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_CMND_TIMERS " "));
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_MQTT D_CMND_TIMERS " %d"), Settings.flag3.timers_enable);
for (byte i = 0; i < MAX_TIMERS; i++) {
timer.data = strtol(p, &p, 10);
p++; // Skip comma
@ -713,7 +721,7 @@ void TimerSaveSettings()
Settings.timer[i].data = timer.data;
if (flag) TimerSetRandomWindow(i);
}
snprintf_P(log_data, sizeof(log_data), PSTR("%s%s0x%08X"), log_data, (i > 0)?",":"", Settings.timer[i].data);
snprintf_P(log_data, sizeof(log_data), PSTR("%s,0x%08X"), log_data, Settings.timer[i].data);
}
AddLog(LOG_LEVEL_DEBUG);
}

View File

@ -253,7 +253,7 @@ bool RulesRuleMatch(byte rule_set, String &event, String &rule)
}
} else match = true;
if (Settings.flag.rules_once) {
if (bitRead(Settings.rule_once, rule_set)) {
if (match) { // Only allow match state changes
if (!bitRead(rules_triggers[rule_set], rules_trigger_count[rule_set])) {
bitSet(rules_triggers[rule_set], rules_trigger_count[rule_set]);
@ -557,44 +557,38 @@ boolean RulesCommand()
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.mems[index -1]);
}
else if ((CMND_ADD == command_code) && (index > 0) && (index <= RULES_MAX_VARS)) {
if ( XdrvMailbox.data_len > 0 ) {
if (XdrvMailbox.data_len > 0) {
double tempvar = CharToDouble(vars[index -1]) + CharToDouble(XdrvMailbox.data);
dtostrfd(tempvar,2,vars[index -1]);
dtostrfd(tempvar, 2, vars[index -1]);
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]);
}
else if ((CMND_SUB == command_code) && (index > 0) && (index <= RULES_MAX_VARS)) {
if ( XdrvMailbox.data_len > 0 ){
if (XdrvMailbox.data_len > 0) {
double tempvar = CharToDouble(vars[index -1]) - CharToDouble(XdrvMailbox.data);
dtostrfd(tempvar,2,vars[index -1]);
dtostrfd(tempvar, 2, vars[index -1]);
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]);
}
else if ((CMND_MULT == command_code) && (index > 0) && (index <= RULES_MAX_VARS)) {
if ( XdrvMailbox.data_len > 0 ){
if (XdrvMailbox.data_len > 0) {
double tempvar = CharToDouble(vars[index -1]) * CharToDouble(XdrvMailbox.data);
dtostrfd(tempvar,2,vars[index -1]);
dtostrfd(tempvar, 2, vars[index -1]);
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]);
}
else if ((CMND_SCALE == command_code) && (index > 0) && (index <= RULES_MAX_VARS)) {
if ( XdrvMailbox.data_len > 0 ) {
if (XdrvMailbox.data_len > 0) {
if (strstr(XdrvMailbox.data, ",")) { // Process parameter entry
double value = 0;
double valueIN = 0;
double fromLow = 0;
double fromHigh = 0;
double toLow = 0;
double toHigh = 0;
char sub_string[XdrvMailbox.data_len +1];
valueIN = CharToDouble(subStr(XdrvMailbox.data, ",", 1));
fromLow = CharToDouble(subStr(XdrvMailbox.data, ",", 2));
fromHigh = CharToDouble(subStr(XdrvMailbox.data, ",", 3));
toLow = CharToDouble(subStr(XdrvMailbox.data, ",", 4));
toHigh = CharToDouble(subStr(XdrvMailbox.data, ",", 5));
value = map_double(valueIN, fromLow, fromHigh, toLow, toHigh);
dtostrfd(value,2,vars[index -1]);
double valueIN = CharToDouble(subStr(sub_string, XdrvMailbox.data, ",", 1));
double fromLow = CharToDouble(subStr(sub_string, XdrvMailbox.data, ",", 2));
double fromHigh = CharToDouble(subStr(sub_string, XdrvMailbox.data, ",", 3));
double toLow = CharToDouble(subStr(sub_string, XdrvMailbox.data, ",", 4));
double toHigh = CharToDouble(subStr(sub_string, XdrvMailbox.data, ",", 5));
double value = map_double(valueIN, fromLow, fromHigh, toLow, toHigh);
dtostrfd(value, 2, vars[index -1]);
}
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, vars[index -1]);
@ -609,25 +603,6 @@ double map_double(double x, double in_min, double in_max, double out_min, double
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
// Function to return a substring defined by a delimiter at an index
char* subStr (char* str, const char *delim, int index) {
char *act, *sub, *ptr;
static char copy[10];
int i;
// Since strtok consumes the first arg, make a copy
strcpy(copy, str);
for (i = 1, act = copy; i <= index; i++, act = NULL) {
sub = strtok_r(act, delim, &ptr);
if (sub == NULL) break;
}
sub = LTrim(sub);
sub = RTrim(sub);
return sub;
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/

View File

@ -196,9 +196,15 @@ const char *device_param_cb[] = {
// Commands
#define D_CMND_KNXTXCMND "KnxTx_Cmnd"
#define D_CMND_KNXTXVAL "KnxTx_Val"
enum KnxCommands { CMND_KNXTXCMND, CMND_KNXTXVAL };
const char kKnxCommands[] PROGMEM = D_CMND_KNXTXCMND "|" D_CMND_KNXTXVAL ;
#define D_CMND_KNX_ENABLED "Knx_Enabled"
#define D_CMND_KNX_ENHANCED "Knx_Enhanced"
#define D_CMND_KNX_PA "Knx_PA"
#define D_CMND_KNX_GA "Knx_GA"
#define D_CMND_KNX_CB "Knx_CB"
enum KnxCommands { CMND_KNXTXCMND, CMND_KNXTXVAL, CMND_KNX_ENABLED, CMND_KNX_ENHANCED, CMND_KNX_PA,
CMND_KNX_GA, CMND_KNX_CB } ;
const char kKnxCommands[] PROGMEM = D_CMND_KNXTXCMND "|" D_CMND_KNXTXVAL "|" D_CMND_KNX_ENABLED "|"
D_CMND_KNX_ENHANCED "|" D_CMND_KNX_PA "|" D_CMND_KNX_GA "|" D_CMND_KNX_CB ;
byte KNX_GA_Search( byte param, byte start = 0 )
{
@ -517,14 +523,27 @@ void KNX_INIT()
void KNX_CB_Action(message_t const &msg, void *arg)
{
device_parameters_t *chan = (device_parameters_t *)arg;
if (!(Settings.flag.knx_enabled)) { return; }
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX D_RECEIVED_FROM " %d.%d.%d " D_COMMAND " %s: %d " D_TO " %s"),
msg.received_on.ga.area, msg.received_on.ga.line, msg.received_on.ga.member,
(msg.ct == KNX_CT_WRITE) ? D_KNX_COMMAND_WRITE : (msg.ct == KNX_CT_READ) ? D_KNX_COMMAND_READ : D_KNX_COMMAND_OTHER,
msg.data[0],
device_param_cb[(chan->type)-1]);
char tempchar[25];
if (msg.data_len == 1) {
// COMMAND
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX D_RECEIVED_FROM " %d.%d.%d " D_COMMAND " %s: %d " D_TO " %s"),
msg.received_on.ga.area, msg.received_on.ga.line, msg.received_on.ga.member,
(msg.ct == KNX_CT_WRITE) ? D_KNX_COMMAND_WRITE : (msg.ct == KNX_CT_READ) ? D_KNX_COMMAND_READ : D_KNX_COMMAND_OTHER,
msg.data[0],
device_param_cb[(chan->type)-1]);
} else {
// VALUE
float tempvar = knx.data_to_2byte_float(msg.data);
dtostrfd(tempvar,2,tempchar);
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_KNX D_RECEIVED_FROM " %d.%d.%d " D_COMMAND " %s: %s " D_TO " %s"),
msg.received_on.ga.area, msg.received_on.ga.line, msg.received_on.ga.member,
(msg.ct == KNX_CT_WRITE) ? D_KNX_COMMAND_WRITE : (msg.ct == KNX_CT_READ) ? D_KNX_COMMAND_READ : D_KNX_COMMAND_OTHER,
tempchar,
device_param_cb[(chan->type)-1]);
}
AddLog(LOG_LEVEL_INFO);
switch (msg.ct)
@ -548,7 +567,13 @@ void KNX_CB_Action(message_t const &msg, void *arg)
{
if (!toggle_inhibit) {
char command[25];
snprintf_P(command, sizeof(command), PSTR("event KNXRX_CMND%d=%d"), ((chan->type) - KNX_SLOT1 + 1 ), msg.data[0]);
if (msg.data_len == 1) {
// Command received
snprintf_P(command, sizeof(command), PSTR("event KNXRX_CMND%d=%d"), ((chan->type) - KNX_SLOT1 + 1 ), msg.data[0]);
} else {
// Value received
snprintf_P(command, sizeof(command), PSTR("event KNXRX_VAL%d=%s"), ((chan->type) - KNX_SLOT1 + 1 ), tempchar);
}
ExecuteCommand(command, SRC_KNX);
if (Settings.flag.knx_enable_enhancement) {
toggle_inhibit = TOGGLE_INHIBIT_TIME;
@ -697,6 +722,7 @@ void KnxSensor(byte sensor_type, float value)
\*********************************************************************************************/
#ifdef USE_WEBSERVER
#ifdef USE_KNX_WEB_MENU
const char S_CONFIGURE_KNX[] PROGMEM = D_CONFIGURE_KNX;
const char HTTP_FORM_KNX[] PROGMEM =
@ -1001,6 +1027,7 @@ void KNX_Save_Settings()
}
}
#endif // USE_KNX_WEB_MENU
#endif // USE_WEBSERVER
@ -1010,14 +1037,12 @@ boolean KnxCommand()
uint8_t index = XdrvMailbox.index;
int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kKnxCommands);
if (!(Settings.flag.knx_enabled)) { return false; }
if (-1 == command_code) { return false; } // Unknown command
else if ((CMND_KNXTXCMND == command_code) && (index > 0) && (index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0)) {
// index <- KNX SLOT to use
// XdrvMailbox.payload <- data to send
if (!(Settings.flag.knx_enabled)) { return false; }
// Search all the registered GA that has that output (variable: KNX SLOTx) as parameter
byte i = KNX_GA_Search(index + KNX_SLOT1 -1);
while ( i != KNX_Empty ) {
@ -1035,12 +1060,14 @@ boolean KnxCommand()
i = KNX_GA_Search(index + KNX_SLOT1 -1, i + 1);
}
snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\"}"),
command, index, XdrvMailbox.data );
}
else if ((CMND_KNXTXVAL == command_code) && (index > 0) && (index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0)) {
// index <- KNX SLOT to use
// XdrvMailbox.payload <- data to send
if (!(Settings.flag.knx_enabled)) { return false; }
// Search all the registered GA that has that output (variable: KNX SLOTx) as parameter
byte i = KNX_GA_Search(index + KNX_SLOT1 -1);
while ( i != KNX_Empty ) {
@ -1062,13 +1089,173 @@ boolean KnxCommand()
i = KNX_GA_Search(index + KNX_SLOT1 -1, i + 1);
}
snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\"}"),
command, index, XdrvMailbox.data );
}
else if (CMND_KNX_ENABLED == command_code) {
if (!XdrvMailbox.data_len) {
if (Settings.flag.knx_enabled) {
snprintf_P(XdrvMailbox.data, sizeof(XdrvMailbox.data), PSTR("1"));
} else {
snprintf_P(XdrvMailbox.data, sizeof(XdrvMailbox.data), PSTR("0"));
}
} else {
if (XdrvMailbox.payload == 1) {
Settings.flag.knx_enabled = 1;
} else if (XdrvMailbox.payload == 0) {
Settings.flag.knx_enabled = 0;
} else { return false; } // Incomplete command
}
snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\"}"),
command, XdrvMailbox.data );
}
else if (CMND_KNX_ENHANCED == command_code) {
if (!XdrvMailbox.data_len) {
if (Settings.flag.knx_enable_enhancement) {
snprintf_P(XdrvMailbox.data, sizeof(XdrvMailbox.data), PSTR("1"));
} else {
snprintf_P(XdrvMailbox.data, sizeof(XdrvMailbox.data), PSTR("0"));
}
} else {
if (XdrvMailbox.payload == 1) {
Settings.flag.knx_enable_enhancement = 1;
} else if (XdrvMailbox.payload == 0) {
Settings.flag.knx_enable_enhancement = 0;
} else { return false; } // Incomplete command
}
snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\"}"),
command, XdrvMailbox.data );
}
else if (CMND_KNX_PA == command_code) {
if (XdrvMailbox.data_len) {
if (strstr(XdrvMailbox.data, ".")) { // Process parameter entry
char sub_string[XdrvMailbox.data_len +1];
int pa_area = atoi(subStr(sub_string, XdrvMailbox.data, ".", 1));
int pa_line = atoi(subStr(sub_string, XdrvMailbox.data, ".", 2));
int pa_member = atoi(subStr(sub_string, XdrvMailbox.data, ".", 3));
if ( ((pa_area == 0) && (pa_line == 0) && (pa_member == 0))
|| (pa_area > 15) || (pa_line > 15) || (pa_member > 255) ) {
snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_ERROR "\"}"),
command );
return true;
} // Invalid command
KNX_addr.pa.area = pa_area;
KNX_addr.pa.line = pa_line;
KNX_addr.pa.member = pa_member;
Settings.knx_physsical_addr = KNX_addr.value;
}
}
KNX_addr.value = Settings.knx_physsical_addr;
snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%d.%d.%d\"}"),
command, KNX_addr.pa.area, KNX_addr.pa.line, KNX_addr.pa.member );
}
else if ((CMND_KNX_GA == command_code) && (index > 0) && (index <= MAX_KNX_GA)) {
if (XdrvMailbox.data_len) {
if (strstr(XdrvMailbox.data, ",")) { // Process parameter entry
char sub_string[XdrvMailbox.data_len +1];
int ga_option = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1));
int ga_area = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2));
int ga_line = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3));
int ga_member = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4));
if ( ((ga_area == 0) && (ga_line == 0) && (ga_member == 0))
|| (ga_area > 31) || (ga_line > 7) || (ga_member > 255)
|| (ga_option < 0) || ((ga_option > KNX_MAX_device_param ) && (ga_option != KNX_Empty))
|| (!device_param[ga_option-1].show) ) {
snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_ERROR "\"}"), command );
return true;
} // Invalid command
KNX_addr.ga.area = ga_area;
KNX_addr.ga.line = ga_line;
KNX_addr.ga.member = ga_member;
if ( index > Settings.knx_GA_registered ) {
Settings.knx_GA_registered ++;
index = Settings.knx_GA_registered;
}
Settings.knx_GA_addr[index -1] = KNX_addr.value;
Settings.knx_GA_param[index -1] = ga_option;
} else {
if ( (XdrvMailbox.payload <= Settings.knx_GA_registered) && (XdrvMailbox.payload > 0) ) {
index = XdrvMailbox.payload;
} else {
snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_ERROR "\"}"), command );
return true;
}
}
if ( index <= Settings.knx_GA_registered ) {
KNX_addr.value = Settings.knx_GA_addr[index -1];
snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s, %d/%d/%d\"}"),
command, index, device_param_ga[Settings.knx_GA_param[index-1]-1],
KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member );
}
} else {
snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%d\"}"),
command, Settings.knx_GA_registered );
}
}
else if ((CMND_KNX_CB == command_code) && (index > 0) && (index <= MAX_KNX_CB)) {
if (XdrvMailbox.data_len) {
if (strstr(XdrvMailbox.data, ",")) { // Process parameter entry
char sub_string[XdrvMailbox.data_len +1];
int cb_option = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1));
int cb_area = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2));
int cb_line = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3));
int cb_member = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4));
if ( ((cb_area == 0) && (cb_line == 0) && (cb_member == 0))
|| (cb_area > 31) || (cb_line > 7) || (cb_member > 255)
|| (cb_option < 0) || ((cb_option > KNX_MAX_device_param ) && (cb_option != KNX_Empty))
|| (!device_param[cb_option-1].show) ) {
snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_ERROR "\"}"), command );
return true;
} // Invalid command
KNX_addr.ga.area = cb_area;
KNX_addr.ga.line = cb_line;
KNX_addr.ga.member = cb_member;
if ( index > Settings.knx_CB_registered ) {
Settings.knx_CB_registered ++;
index = Settings.knx_CB_registered;
}
Settings.knx_CB_addr[index -1] = KNX_addr.value;
Settings.knx_CB_param[index -1] = cb_option;
} else {
if ( (XdrvMailbox.payload <= Settings.knx_CB_registered) && (XdrvMailbox.payload > 0) ) {
index = XdrvMailbox.payload;
} else {
snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"" D_ERROR "\"}"), command );
return true;
}
}
if ( index <= Settings.knx_CB_registered ) {
KNX_addr.value = Settings.knx_CB_addr[index -1];
snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s, %d/%d/%d\"}"),
command, index, device_param_cb[Settings.knx_CB_param[index-1]-1],
KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member );
}
} else {
snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%d\"}"),
command, Settings.knx_CB_registered );
}
}
else { return false; } // Incomplete command
snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\"}"),
command, index, XdrvMailbox.data );
return true;
}

3
sonoff/xsns_09_bmp.ino Normal file → Executable file
View File

@ -459,6 +459,9 @@ void BmpRead()
#endif // USE_BME680
}
if (bmp_temperature != 0.0) { bmp_temperature = ConvertTemp(bmp_temperature); }
glob_humidity = bmp_humidity;
glob_temperature = bmp_temperature;
}
void BmpEverySecond()

9
sonoff/xsns_14_sht3x.ino Normal file → Executable file
View File

@ -101,9 +101,16 @@ void Sht3xShow(boolean json)
char types[11];
for (byte i = 0; i < sht3x_count; i++) {
if (Sht3xRead(t, h, sht3x_sensors[i].address)) {
if (0 == i) {
glob_humidity = h;
glob_temperature = t;
}
dtostrfd(t, Settings.flag2.temperature_resolution, temperature);
dtostrfd(h, Settings.flag2.humidity_resolution, humidity);
snprintf_P(types, sizeof(types), PSTR("%s-0x%02X"), sht3x_sensors[i].types, sht3x_sensors[i].address); // "SHT3X-0xXX"
if (json) {
snprintf_P(mqtt_data, sizeof(mqtt_data), JSON_SNS_TEMPHUM, mqtt_data, types, temperature, humidity);
#ifdef USE_DOMOTICZ
@ -159,4 +166,4 @@ boolean Xsns14(byte function)
}
#endif // USE_SHT3X
#endif // USE_I2C
#endif // USE_I2C

View File

@ -108,7 +108,7 @@ boolean Xsns26(byte function)
if (i2c_flg) {
switch (function) {
case FUNC_PREP_BEFORE_TELEPERIOD:
case FUNC_EVERY_SECOND:
LM75ADDetect();
break;
case FUNC_JSON_APPEND:

336
sonoff/xsns_29_mcp230xx.ino Normal file
View File

@ -0,0 +1,336 @@
/*
xsns_29_mcp230xx.ino - Support for I2C MCP23008/MCP23017 GPIO Expander (INPUT ONLY!)
Copyright (C) 2018 Andre Thomas and Theo Arends
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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_I2C
#ifdef USE_MCP230xx
/*********************************************************************************************\
MCP23008/17 - I2C GPIO EXPANDER
Docs at https://www.microchip.com/wwwproducts/en/MCP23008
https://www.microchip.com/wwwproducts/en/MCP23017
I2C Address: 0x20 - 0x27
\*********************************************************************************************/
#define XSNS_29 29
#define MCP230xx_ADDRESS1 0x20
#define MCP230xx_ADDRESS2 0x21
#define MCP230xx_ADDRESS3 0x22
#define MCP230xx_ADDRESS4 0x23
#define MCP230xx_ADDRESS5 0x24
#define MCP230xx_ADDRESS6 0x25
#define MCP230xx_ADDRESS7 0x26
#define MCP230xx_ADDRESS8 0x27
/*
Default register locations for MCP23008 - They change for MCP23017 in default bank mode
*/
uint8_t MCP230xx_IODIR = 0x00;
uint8_t MCP230xx_GPINTEN = 0x02;
uint8_t MCP230xx_IOCON = 0x05;
uint8_t MCP230xx_GPPU = 0x06;
uint8_t MCP230xx_INTF = 0x07;
uint8_t MCP230xx_INTCAP = 0x08;
uint8_t MCP230xx_GPIO = 0x09;
uint8_t mcp230xx_type = 0;
uint8_t mcp230xx_address;
uint8_t mcp230xx_addresses[] = { MCP230xx_ADDRESS1, MCP230xx_ADDRESS2, MCP230xx_ADDRESS3, MCP230xx_ADDRESS4, MCP230xx_ADDRESS5, MCP230xx_ADDRESS6, MCP230xx_ADDRESS7, MCP230xx_ADDRESS8 };
uint8_t mcp230xx_pincount = 0;
uint8_t mcp230xx_int_en = 0;
const char MCP230XX_SENSOR_RESPONSE[] PROGMEM = "{\"Sensor29\":{\"D\":%i,\"MODE\":%i,\"PULL-UP\":%i}}";
uint8_t MCP230xx_readGPIO(uint8_t port) {
return I2cRead8(mcp230xx_address, MCP230xx_GPIO + port);
}
void MCP230xx_ApplySettings(void) {
uint8_t reg_gppu = 0;
uint8_t reg_gpinten = 0;
uint8_t reg_iodir = 0xFF;
uint8_t int_en = 0;
for (uint8_t idx = 0; idx < 8; idx++) {
switch (Settings.mcp230xx_config[idx].pinmode) {
case 0 ... 1:
reg_iodir |= (1 << idx);
break;
case 2 ... 4:
reg_iodir |= (1 << idx);
reg_gpinten |= (1 << idx);
int_en=1;
break;
default:
break;
}
if (Settings.mcp230xx_config[idx].pullup) {
reg_gppu |= (1 << idx);
}
}
I2cWrite8(mcp230xx_address, MCP230xx_GPPU, reg_gppu);
I2cWrite8(mcp230xx_address, MCP230xx_GPINTEN, reg_gpinten);
I2cWrite8(mcp230xx_address, MCP230xx_IODIR, reg_iodir);
if (mcp230xx_type == 2) { // We have a MCP23017
reg_gppu = 0;
reg_gpinten = 0;
reg_iodir = 0xFF;
for (uint8_t idx = 8; idx < 16; idx++) {
switch (Settings.mcp230xx_config[idx].pinmode) {
case 0 ... 1:
reg_iodir |= (1 << idx - 8);
break;
case 2 ... 4:
reg_iodir |= (1 << idx - 8);
reg_gpinten |= (1 << idx - 8);
int_en=1;
break;
default:
break;
}
if (Settings.mcp230xx_config[idx].pullup) {
reg_gppu |= (1 << idx - 8);
}
}
I2cWrite8(mcp230xx_address, MCP230xx_GPPU + 1, reg_gppu);
I2cWrite8(mcp230xx_address, MCP230xx_GPINTEN + 1, reg_gpinten);
I2cWrite8(mcp230xx_address, MCP230xx_IODIR + 1, reg_iodir);
}
mcp230xx_int_en=int_en;
}
void MCP230xx_Detect()
{
uint8_t buffer;
if (mcp230xx_type) {
return;
}
for (byte i = 0; i < sizeof(mcp230xx_addresses); i++) {
mcp230xx_address = mcp230xx_addresses[i];
I2cWrite8(mcp230xx_address, MCP230xx_IOCON, 0x80); // attempt to set bank mode - this will only work on MCP23017, so its the best way to detect the different chips 23008 vs 23017
if (I2cValidRead8(&buffer, mcp230xx_address, MCP230xx_IOCON)) {
if (buffer == 0x00) {
mcp230xx_type = 1; // We have a MCP23008
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "MCP23008", mcp230xx_address);
AddLog(LOG_LEVEL_DEBUG);
mcp230xx_pincount = 8;
MCP230xx_ApplySettings();
} else {
if (buffer == 0x80) {
mcp230xx_type = 2; // We have a MCP23017
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "MCP23017", mcp230xx_address);
AddLog(LOG_LEVEL_DEBUG);
mcp230xx_pincount = 16;
// Reset bank mode to 0
I2cWrite8(mcp230xx_address, MCP230xx_IOCON, 0x00);
// Update register locations for MCP23017
MCP230xx_GPINTEN = 0x04;
MCP230xx_GPPU = 0x0C;
MCP230xx_INTF = 0x0E;
MCP230xx_INTCAP = 0x10;
MCP230xx_GPIO = 0x12;
MCP230xx_ApplySettings();
}
}
break;
}
}
}
bool MCP230xx_CheckForInterrupt(void) {
uint8_t intf;
uint8_t mcp230xx_intcap = 0;
uint8_t report_int;
if (I2cValidRead8(&intf, mcp230xx_address, MCP230xx_INTF)) {
if (intf > 0) {
if (I2cValidRead8(&mcp230xx_intcap, mcp230xx_address, MCP230xx_INTCAP)) {
for (uint8_t intp = 0; intp < 8; intp++) {
if ((intf >> intp) & 0x01) { // we know which pin caused interrupt
report_int = 0;
if (Settings.mcp230xx_config[intp].pinmode > 1) {
switch (Settings.mcp230xx_config[intp].pinmode) {
case 2:
report_int = 1;
break;
case 3:
if (((mcp230xx_intcap >> intp) & 0x01) == 0) report_int = 1; // Int on LOW
break;
case 4:
if (((mcp230xx_intcap >> intp) & 0x01) == 1) report_int = 1; // Int on HIGH
break;
default:
break;
}
if (report_int) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str());
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"MCP230XX_INT\":{\"Pin\":\"D%i\", \"State\":%i}"), mqtt_data, intp, ((mcp230xx_intcap >> intp) & 0x01));
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data);
MqttPublishPrefixTopic_P(RESULT_OR_STAT, mqtt_data);
}
}
}
}
}
}
}
if (mcp230xx_type == 2) { // We have a MCP23017 so we need to check the other 8 bits also
if (I2cValidRead8(&intf, mcp230xx_address, MCP230xx_INTF+1)) {
if (intf > 0) {
if (I2cValidRead8(&mcp230xx_intcap, mcp230xx_address, MCP230xx_INTCAP+1)) {
for (uint8_t intp = 0; intp < 8; intp++) {
if ((intf >> intp) & 0x01) { // we know which pin caused interrupt
report_int = 0;
if (Settings.mcp230xx_config[intp+8].pinmode > 1) { // change on INT
switch (Settings.mcp230xx_config[intp+8].pinmode) {
case 2:
report_int = 1;
break;
case 3:
if (((mcp230xx_intcap >> intp) & 0x01) == 0) report_int = 1; // Int on LOW
break;
case 4:
if (((mcp230xx_intcap >> intp) & 0x01) == 1) report_int = 1; // Int on HIGH
break;
default:
break;
}
}
if (report_int) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str());
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"MCP230XX_INT\":{\"Pin\":\"D%i\", \"State\":%i}"), mqtt_data, intp+8, ((mcp230xx_intcap >> intp) & 0x01));
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data);
MqttPublishPrefixTopic_P(RESULT_OR_STAT, mqtt_data);
}
}
}
}
}
}
}
}
void MCP230xx_Show(boolean json)
{
if (mcp230xx_type) {
if (json) {
if (mcp230xx_type == 1) {
uint8_t gpio = MCP230xx_readGPIO(0);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"MCP23008\":{\"D0\":%i,\"D1\":%i,\"D2\":%i,\"D3\":%i,\"D4\":%i,\"D5\":%i,\"D6\":%i,\"D7\":%i}"),
mqtt_data,(gpio>>0)&1,(gpio>>1)&1,(gpio>>2)&1,(gpio>>3)&1,(gpio>>4)&1,(gpio>>5)&1,(gpio>>6)&1,(gpio>>7)&1);
}
if (mcp230xx_type == 2) {
uint8_t gpio1 = MCP230xx_readGPIO(0);
uint8_t gpio2 = MCP230xx_readGPIO(1);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"MCP23017\":{\"D0\":%i,\"D1\":%i,\"D2\":%i,\"D3\":%i,\"D4\":%i,\"D5\":%i,\"D6\":%i,\"D7\":%i,\"D8\":%i,\"D9\":%i,\"D10\":%i,\"D11\":%i,\"D12\":%i,\"D13\":%i,\"D14\":%i,\"D15\":%i}"),
mqtt_data, (gpio1>>0)&1,(gpio1>>1)&1,(gpio1>>2)&1,(gpio1>>3)&1,(gpio1>>4)&1,(gpio1>>5)&1,(gpio1>>6)&1,(gpio1>>7)&1,(gpio2>>0)&1,(gpio2>>1)&1,(gpio2>>2)&1,(gpio2>>3)&1,(gpio2>>4)&1,(gpio2>>5)&1,(gpio2>>6)&1,(gpio2>>7)&1);
}
}
}
}
bool MCP230xx_Command(void) {
boolean serviced = true;
uint8_t _a, _b = 0;
uint8_t pin, pinmode, pullup = 0;
String data = XdrvMailbox.data;
data.toUpperCase();
if (data == "RESET") { // we need to reset all pins to input
for (uint8_t pinx=0;pinx<16;pinx++) {
Settings.mcp230xx_config[pinx].pinmode=1;
Settings.mcp230xx_config[pinx].pullup=0;
Settings.mcp230xx_config[pinx].b4=0;
Settings.mcp230xx_config[pinx].b5=0;
Settings.mcp230xx_config[pinx].b6=0;
Settings.mcp230xx_config[pinx].b7=0;
}
MCP230xx_ApplySettings();
snprintf_P(mqtt_data, sizeof(mqtt_data), MCP230XX_SENSOR_RESPONSE,99,99,99);
return serviced;
}
_a = data.indexOf(",");
pin = data.substring(0, _a).toInt();
if (pin < mcp230xx_pincount) {
String cmnd = data.substring(_a+1);
if (cmnd == "?") {
snprintf_P(mqtt_data, sizeof(mqtt_data), MCP230XX_SENSOR_RESPONSE,pin,Settings.mcp230xx_config[pin].pinmode,Settings.mcp230xx_config[pin].pullup);
return serviced;
}
}
_b = data.indexOf(",", _a + 1);
if (_a < XdrvMailbox.data_len) {
if (_b < XdrvMailbox.data_len) {
pinmode = data.substring(_a+1, _b).toInt();
pullup = data.substring(_b+1, XdrvMailbox.data_len).toInt();
if ((pin < mcp230xx_pincount) && (pinmode < 5) && (pullup < 2)) {
Settings.mcp230xx_config[pin].pinmode=pinmode;
Settings.mcp230xx_config[pin].pullup=pullup;
MCP230xx_ApplySettings();
snprintf_P(mqtt_data, sizeof(mqtt_data), MCP230XX_SENSOR_RESPONSE,pin,pinmode,pullup);
} else {
serviced = false;
}
} else {
serviced = false;
}
} else {
serviced = false;
}
return serviced;
}
/*********************************************************************************************\
Interface
\*********************************************************************************************/
boolean Xsns29(byte function)
{
boolean result = false;
if (i2c_flg) {
switch (function) {
case FUNC_EVERY_SECOND:
MCP230xx_Detect();
break;
case FUNC_EVERY_50_MSECOND:
if (mcp230xx_int_en) { // Only check for interrupts if its enabled on one of the pins
MCP230xx_CheckForInterrupt();
}
break;
case FUNC_JSON_APPEND:
MCP230xx_Show(1);
break;
case FUNC_COMMAND:
if (XSNS_29 == XdrvMailbox.index) {
result = MCP230xx_Command();
}
break;
default:
break;
}
}
return result;
}
#endif // USE_MCP230xx
#endif // USE_I2C

448
sonoff/xsns_30_mpr121.ino Normal file
View File

@ -0,0 +1,448 @@
/**
*
* @file xsns_30_mpr121.ino
*
* @package Sonoff-Tasmota
* @subpackage Sensors
* @name MPR121
*
* @description Driver for up to 4x Freescale MPR121 Proximity Capacitive Touch Sensor Controllers (Only touch buttons).
*
* @author Rene 'Renne' Bartsch, B.Sc. Informatics, <rene@bartschnet.de>
* @copyright Rene 'Renne' Bartsch 2018
* @date $Date$
* @version $Id$
*
* @link https://github.com/arendst/Sonoff-Tasmota/wiki/MPR121 \endlink
* @link https://www.sparkfun.com/datasheets/Components/MPR121.pdf \endlink
* @link http://cache.freescale.com/files/sensors/doc/app_note/AN3891.pdf \endlink
* @link http://cache.freescale.com/files/sensors/doc/app_note/AN3892.pdf \endlink
* @link http://cache.freescale.com/files/sensors/doc/app_note/AN3893.pdf \endlink
* @link http://cache.freescale.com/files/sensors/doc/app_note/AN3894.pdf \endlink
* @link http://cache.freescale.com/files/sensors/doc/app_note/AN3895.pdf \endlink
*
* @license GNU GPL v.3
*/
/*
* 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_I2C
#ifdef USE_MPR121
/** @defgroup group1 MPR121
* MPR121 preprocessor directives
* @{
*/
/** Electrodes Status Register. */
#define MPR121_ELEX_REG 0x00
/** ELEC0-11 Maximum Half Delta Rising Register. */
#define MPR121_MHDR_REG 0x2B
/** ELEC0-11 Maximum Half Delta Rising Value. */
#define MPR121_MHDR_VAL 0x01
/** ELEC0-11 Noise Half Delta Falling Register. */
#define MPR121_NHDR_REG 0x2C
/** ELEC0-11 Noise Half Delta Falling Value. */
#define MPR121_NHDR_VAL 0x01
/** ELEC0-11 Noise Count Limit Rising Register. */
#define MPR121_NCLR_REG 0x2D
/** ELEC0-11 Noise Count Limit Rising Value. */
#define MPR121_NCLR_VAL 0x0E
/** ELEC0-11 Maximum Half Delta Falling Register. */
#define MPR121_MHDF_REG 0x2F
/** ELEC0-11 Maximum Half Delta Falling Value. */
#define MPR121_MHDF_VAL 0x01
/** ELEC0-11 Noise Half Delta Falling Register. */
#define MPR121_NHDF_REG 0x30
/** ELEC0-11 Noise Half Delta Falling Value. */
#define MPR121_NHDF_VAL 0x05
/** ELEC0-11 Noise Count Limit Falling Register. */
#define MPR121_NCLF_REG 0x31
/** ELEC0-11 Noise Count Limit Falling Value. */
#define MPR121_NCLF_VAL 0x01
/** Proximity Maximum Half Delta Rising Register. */
#define MPR121_MHDPROXR_REG 0x36
/** Proximity Maximum Half Delta Rising Value. */
#define MPR121_MHDPROXR_VAL 0x3F
/** Proximity Noise Half Delta Rising Register. */
#define MPR121_NHDPROXR_REG 0x37
/** Proximity Noise Half Delta Rising Value. */
#define MPR121_NHDPROXR_VAL 0x5F
/** Proximity Noise Count Limit Rising Register. */
#define MPR121_NCLPROXR_REG 0x38
/** Proximity Noise Count Limit Rising Value. */
#define MPR121_NCLPROXR_VAL 0x04
/** Proximity Filter Delay Count Limit Rising Register. */
#define MPR121_FDLPROXR_REG 0x39
/** Proximity Filter Delay Count Limit Rising Value. */
#define MPR121_FDLPROXR_VAL 0x00
/** Proximity Maximum Half Delta Falling Register. */
#define MPR121_MHDPROXF_REG 0x3A
/** Proximity Maximum Half Delta Falling Value. */
#define MPR121_MHDPROXF_VAL 0x01
/** Proximity Noise Half Delta Falling Register. */
#define MPR121_NHDPROXF_REG 0x3B
/** Proximity Noise Half Delta Falling Value. */
#define MPR121_NHDPROXF_VAL 0x01
/** Proximity Noise Count Limit Falling Register. */
#define MPR121_NCLPROXF_REG 0x3C
/** Proximity Noise Count Limit Falling Value. */
#define MPR121_NCLPROXF_VAL 0x1F
/** Proximity Filter Delay Count Limit Falling Register. */
#define MPR121_FDLPROXF_REG 0x3D
/** Proximity Filter Delay Count Limit Falling Value. */
#define MPR121_FDLPROXF_VAL 0x04
/** First Electrode Touch Threshold Register. */
#define MPR121_E0TTH_REG 0x41
/** First Electrode Touch Threshold Value. */
#define MPR121_E0TTH_VAL 12
/** First Electrode Release Threshold Register. */
#define MPR121_E0RTH_REG 0x42
/** First Electrode Release Threshold Value. */
#define MPR121_E0RTH_VAL 6
/** Global CDC/CDT Configuration Register. */
#define MPR121_CDT_REG 0x5D
/** Global CDC/CDT Configuration Value. */
#define MPR121_CDT_VAL 0x20
/** Enable electrodes Register. */
#define MPR121_ECR_REG 0x5E
/** Enable electrodes Value. */
#define MPR121_ECR_VAL 0x8F // Start ELE0-11 with first 5 bits of baseline tracking
//#define MPR121_ECR_VAL 0xBF // Start ELE0-11 + proximity with first 5 bits of baseline tracking
/** Soft-reset Register. */
#define MPR121_SRST_REG 0x80
/** Soft-reset Value. */
#define MPR121_SRST_VAL 0x63
/** Get bit of variable 'current' of sensor at position. */
#define BITC(sensor, position) ((pS->current[sensor] >> position) & 1)
/** Get bit of variable 'previous' of sensor at position. */
#define BITP(sensor, position) ((pS->previous[sensor] >> position) & 1)
/**@}*/
/**
* MPR121 sensors status and data struct.
*
* The struct mpr121 uses the indices of i2c_addr and id to link the specific sensors to an I2C address and a human-readable ID
* and the indices of the arrays connected, running, current and previous to store sensor status and data of a specific sensor.
*
*/
typedef struct mpr121 mpr121;
struct mpr121 {
const uint8_t i2c_addr[4] = { 0x5A, 0x5B, 0x5C, 0x5D }; /** I2C addresses of MPR121 controller */
const char id[4] = { 'A', 'B', 'C', 'D' }; /** Human-readable sensor IDs*/
bool connected[4] = { false, false, false, false }; /** Status if sensor is connected at I2C address */
bool running[4] = { false, false, false, false }; /** Running state of sensor */
uint16_t current[4] = { 0x0000, 0x0000, 0x0000, 0x0000 }; /** Current values in electrode register of sensor */
uint16_t previous[4] = { 0x0000, 0x0000, 0x0000, 0x0000 }; /** Current values in electrode register of sensor */
};
/**
* The function Mpr121Init() soft-resets, detects and configures up to 4x MPR121 sensors.
*
* @param struct *pS Struct with MPR121 status and data.
* @return void
* @pre None.
* @post None.
*
*/
void Mpr121Init(struct mpr121 *pS)
{
// Loop through I2C addresses
for (uint8_t i = 0; i < sizeof(pS->i2c_addr[i]); i++) {
// Soft reset sensor and check if connected at I2C address
pS->connected[i] = (I2cWrite8(pS->i2c_addr[i], MPR121_SRST_REG, MPR121_SRST_VAL)
&& (0x24 == I2cRead8(pS->i2c_addr[i], 0x5D)));
if (pS->connected[i]) {
// Log sensor found
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121(%c) " D_FOUND_AT " 0x%X"), pS->id[i], pS->i2c_addr[i]);
AddLog(LOG_LEVEL_INFO);
// Set thresholds for registers 0x41 - 0x5A (ExTTH and ExRTH)
for (uint8_t j = 0; j < 13; j++) {
// Touch
I2cWrite8(pS->i2c_addr[i], MPR121_E0TTH_REG + 2 * j, MPR121_E0TTH_VAL);
// Release
I2cWrite8(pS->i2c_addr[i], MPR121_E0RTH_REG + 2 * j, MPR121_E0RTH_VAL);
}
// ELEC0-11 Maximum Half Delta Rising
I2cWrite8(pS->i2c_addr[i], MPR121_MHDR_REG, MPR121_MHDR_VAL);
// ELEC0-11 Noise Half Delta Rising
I2cWrite8(pS->i2c_addr[i], MPR121_NHDR_REG, MPR121_NHDR_VAL);
// ELEC0-11 Noise Count Limit Rising
I2cWrite8(pS->i2c_addr[i], MPR121_NCLR_REG, MPR121_NCLR_VAL);
// ELEC0-11 Maximum Half Delta Falling
I2cWrite8(pS->i2c_addr[i], MPR121_MHDF_REG, MPR121_MHDF_VAL);
// ELEC0-11 Noise Half Delta Falling
I2cWrite8(pS->i2c_addr[i], MPR121_NHDF_REG, MPR121_NHDF_VAL);
// ELEC0-11 Noise Count Limit Falling
I2cWrite8(pS->i2c_addr[i], MPR121_NCLF_REG, MPR121_NCLF_VAL);
// Proximity Maximum Half Delta Rising
I2cWrite8(pS->i2c_addr[i], MPR121_MHDPROXR_REG, MPR121_MHDPROXR_VAL);
// Proximity Noise Half Delta Rising
I2cWrite8(pS->i2c_addr[i], MPR121_NHDPROXR_REG, MPR121_NHDPROXR_VAL);
// Proximity Noise Count Limit Rising
I2cWrite8(pS->i2c_addr[i], MPR121_NCLPROXR_REG, MPR121_NCLPROXR_VAL);
// Proximity Filter Delay Count Limit Rising
I2cWrite8(pS->i2c_addr[i], MPR121_FDLPROXR_REG, MPR121_FDLPROXR_VAL);
// Proximity Maximum Half Delta Falling
I2cWrite8(pS->i2c_addr[i], MPR121_MHDPROXF_REG, MPR121_MHDPROXF_VAL);
// Proximity Noise Half Delta Falling
I2cWrite8(pS->i2c_addr[i], MPR121_NHDPROXF_REG, MPR121_NHDPROXF_VAL);
// Proximity Noise Count Limit Falling
I2cWrite8(pS->i2c_addr[i], MPR121_NCLPROXF_REG, MPR121_NCLPROXF_VAL);
// Proximity Filter Delay Count Limit Falling
I2cWrite8(pS->i2c_addr[i], MPR121_FDLPROXF_REG, MPR121_FDLPROXF_VAL);
// Global CDC/CDT Configuration
I2cWrite8(pS->i2c_addr[i], MPR121_CDT_REG, MPR121_CDT_VAL);
// Enable sensor
I2cWrite8(pS->i2c_addr[i], MPR121_ECR_REG, MPR121_ECR_VAL);
// Check if sensor is running
pS->running[i] = (0x00 != I2cRead8(pS->i2c_addr[i], MPR121_ECR_REG));
if (pS->running[i]) {
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121%c: Running"), pS->id[i]);
} else {
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121%c: NOT Running"), pS->id[i]);
}
AddLog(LOG_LEVEL_INFO);
} else {
// Make sure running is false
pS->running[i] = false;
}
} // for-loop
// Display no sensor found message
if (!(pS->connected[0] || pS->connected[1] || pS->connected[2]
|| pS->connected[3])) {
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121: No sensors found"));
AddLog(LOG_LEVEL_DEBUG);
}
} // void Mpr121Init(struct mpr121 *s)
/**
* Publishes the sensor information.
*
* The function Mpr121Show() reads sensor data, checks for over-current exceptions and
* creates strings with button states for the web-interface and near real-time/ telemetriy MQTT.
*
* @param struct *pS Struct with MPR121 status and data.
* @param byte function Tasmota function ID.
* @return void
* @pre Call Mpr121Init() once.
* @post None.
*
*/
void Mpr121Show(struct mpr121 *pS, byte function)
{
// Loop through sensors
for (uint8_t i = 0; i < sizeof(pS->i2c_addr[i]); i++) {
// Check if sensor is connected
if (pS->connected[i]) {
// Read data
if (!I2cValidRead16LE(&pS->current[i], pS->i2c_addr[i], MPR121_ELEX_REG)) {
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_I2C "MPR121%c: ERROR: Cannot read data!"), pS->id[i]);
AddLog(LOG_LEVEL_ERROR);
Mpr121Init(pS);
return;
}
// Check if OVCF bit is set
if (BITC(i, 15)) {
// Clear OVCF bit
I2cWrite8(pS->i2c_addr[i], MPR121_ELEX_REG, 0x00);
snprintf_P(log_data, sizeof(log_data),
PSTR(D_LOG_I2C "MPR121%c: ERROR: Excess current detected! Fix circuits if it happens repeatedly! Soft-resetting MPR121 ..."), pS->id[i]);
AddLog(LOG_LEVEL_ERROR);
Mpr121Init(pS);
return;
}
}
// Check if sensor is running
if (pS->running[i]) {
// Append sensor to JSON message string
if (FUNC_JSON_APPEND == function) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"MPR121%c\":{"), mqtt_data, pS->id[i]);
}
// Loop through buttons
for (uint8_t j = 0; j < 13; j++) {
// Add sensor, button and state to MQTT JSON message string
if ((FUNC_EVERY_50_MSECOND == function)
&& (BITC(i, j) != BITP(i, j))) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"MPR121%c\":{\"Button%i\":%i}}"), pS->id[i], j, BITC(i, j));
MqttPublishPrefixTopic_P(RESULT_OR_STAT, mqtt_data);
}
// Add buttons to web string
#ifdef USE_WEBSERVER
if (FUNC_WEB_APPEND == function) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s{s}MPR121%c Button%d{m}%d{e}"), mqtt_data, pS->id[i], j, BITC(i, j));
}
#endif // USE_WEBSERVER
// Append JSON message string
if (FUNC_JSON_APPEND == function) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"Button%i\":%i,"), mqtt_data, j, BITC(i, j));
}
} // for-loop j
// Save sensor data
pS->previous[i] = pS->current[i];
// Append JSON message string
if (FUNC_JSON_APPEND == function) {
snprintf_P(mqtt_data, sizeof(mqtt_data), "%s}", mqtt_data);
}
} // if->running
} // for-loop i
} // void Mpr121Show(byte function)
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
/**
* @ingroup group1
* Assign Tasmota sensor model ID
*/
#define XSNS_30
/**
* The function Xsns30() interfaces Tasmota with the driver.
*
* It provides the function IDs
* FUNC_INIT to initialize a driver,
* FUNC_EVERY_50_MSECOND for near real-time operation,
* FUNC_JSON_APPEND for telemetry data and
* FUNC_WEB_APPEND for displaying data in the Tasmota web-interface
*
* @param byte function Tasmota function ID.
* @return boolean ???
* @pre None.
* @post None.
*
*/
boolean Xsns30(byte function)
{
// ???
boolean result = false;
// Sensor state/data struct
static struct mpr121 mpr121;
// Check if I2C is enabled
if (i2c_flg) {
switch (function) {
// Initialize Sensors
case FUNC_INIT:
Mpr121Init(&mpr121);
break;
// Run ever 50 milliseconds (near real-time functions)
case FUNC_EVERY_50_MSECOND:
Mpr121Show(&mpr121, FUNC_EVERY_50_MSECOND);
break;
// Generate JSON telemetry string
case FUNC_JSON_APPEND:
Mpr121Show(&mpr121, FUNC_JSON_APPEND);
break;
#ifdef USE_WEBSERVER
// Show sensor data on main web page
case FUNC_WEB_APPEND:
Mpr121Show(&mpr121, FUNC_WEB_APPEND);
break;
#endif // USE_WEBSERVER
}
}
// Return boolean result
return result;
}
#endif // USE_MPR121
#endif // USE_I2C

132
sonoff/xsns_31_ccs811.ino Normal file
View File

@ -0,0 +1,132 @@
/*
xsns_31_ccs811.ino - CCS811 gas and air quality sensor support for Sonoff-Tasmota
Copyright (C) 2018 Gerhard Mutz and Theo Arends
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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_I2C
#ifdef USE_CCS811
/*********************************************************************************************\
* SGP30 - Gas (TVOC - Total Volatile Organic Compounds) and Air Quality (CO2)
*
* Source: Adafruit
*
* I2C Address: 0x5A assumes ADDR connected to Gnd, Wake also must be grounded
\*********************************************************************************************/
#include "Adafruit_CCS811.h"
Adafruit_CCS811 ccs;
uint8_t CCS811_ready;
uint8_t CCS811_type;
uint16_t eCO2;
uint16_t TVOC;
uint8_t tcnt = 0;
uint8_t ecnt = 0;
/********************************************************************************************/
#define EVERYNSECONDS 5
void CCS811Update() // Perform every n second
{
tcnt++;
if (tcnt >= EVERYNSECONDS) {
tcnt = 0;
CCS811_ready = 0;
if (!CCS811_type) {
sint8_t res = ccs.begin(CCS811_ADDRESS);
if (!res) {
CCS811_type = 1;
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "CCS811", 0x5A);
AddLog(LOG_LEVEL_DEBUG);
} else {
//snprintf_P(log_data, sizeof(log_data), "CCS811 init failed: %d",res);
//AddLog(LOG_LEVEL_DEBUG);
}
} else {
if (ccs.available()) {
if (!ccs.readData()){
TVOC = ccs.getTVOC();
eCO2 = ccs.geteCO2();
CCS811_ready = 1;
if ((glob_humidity != 0) && (glob_temperature != -9999)) {
double gtmp = glob_temperature * 4;
ccs.setEnvironmentalData(glob_humidity, gtmp / 4);
}
ecnt = 0;
}
} else {
// failed, count up
ecnt++;
if (ecnt > 6) {
// after 30 seconds, restart
ccs.begin(CCS811_ADDRESS);
}
}
}
}
}
const char HTTP_SNS_CCS811[] PROGMEM = "%s"
"{s}CCS811 " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
"{s}CCS811 " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}";
void CCS811Show(boolean json)
{
if (CCS811_ready) {
if (json) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"CCS811\":{\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TVOC "\":%d}"), mqtt_data,eCO2,TVOC);
#ifdef USE_DOMOTICZ
if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, eCO2);
#endif // USE_DOMOTICZ
#ifdef USE_WEBSERVER
} else {
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_CCS811, mqtt_data, eCO2, TVOC);
#endif
}
}
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
#define XSNS_31
boolean Xsns31(byte function)
{
boolean result = false;
if (i2c_flg) {
switch (function) {
case FUNC_EVERY_SECOND:
CCS811Update();
break;
case FUNC_JSON_APPEND:
CCS811Show(1);
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_APPEND:
CCS811Show(0);
break;
#endif // USE_WEBSERVER
}
}
return result;
}
#endif // USE_CCS811
#endif // USE_I2C

View File

@ -146,41 +146,45 @@ else:
def StartDecode():
# print("Decoding\n{}".format(obj))
if ("Time" in obj["StatusSNS"]):
time = str(" from status report taken at {}".format(obj["StatusSNS"]["Time"]))
if ("FriendlyName" in obj["Status"]):
print("\nDecoding information for device {}{}".format(obj["Status"]["FriendlyName"][0], time))
if ("StatusSNS" in obj):
if ("Time" in obj["StatusSNS"]):
time = str(" from status report taken at {}".format(obj["StatusSNS"]["Time"]))
if ("SetOption" in obj["StatusLOG"]):
options = []
option = obj["StatusLOG"]["SetOption"][0]
i_option = int(option,16)
for i in range(len(a_setoption)):
if (a_setoption[i]):
state = (i_option >> i) & 1
options.append(str("{0:2d} ({1}) {2}".format(i, a_on_off[state], a_setoption[i])))
if ("Status" in obj):
if ("FriendlyName" in obj["Status"]):
print("\nDecoding information for device {}{}".format(obj["Status"]["FriendlyName"][0], time))
print("\nOptions")
for i in range(len(options)):
print(" {}".format(options[i]))
if ("StatusLOG" in obj):
if ("SetOption" in obj["StatusLOG"]):
options = []
option = obj["StatusLOG"]["SetOption"][0]
i_option = int(option,16)
for i in range(len(a_setoption)):
if (a_setoption[i]):
state = (i_option >> i) & 1
options.append(str("{0:2d} ({1}) {2}".format(i, a_on_off[state], a_setoption[i])))
print("\nOptions")
for i in range(len(options)):
print(" {}".format(options[i]))
if ("Features" in obj["StatusMEM"]):
features = []
for f in range(5):
feature = obj["StatusMEM"]["Features"][f]
i_feature = int(feature,16)
if (f == 0):
features.append(str("Language LCID = {}".format(i_feature & 0xFFFF)))
else:
for i in range(len(a_features[f -1])):
if ((i_feature >> i) & 1):
features.append(a_features[f -1][i])
if ("StatusMEM" in obj):
if ("Features" in obj["StatusMEM"]):
features = []
for f in range(5):
feature = obj["StatusMEM"]["Features"][f]
i_feature = int(feature,16)
if (f == 0):
features.append(str("Language LCID = {}".format(i_feature & 0xFFFF)))
else:
for i in range(len(a_features[f -1])):
if ((i_feature >> i) & 1):
features.append(a_features[f -1][i])
features.sort()
print("\nFeatures")
for i in range(len(features)):
print(" {}".format(features[i]))
features.sort()
print("\nFeatures")
for i in range(len(features)):
print(" {}".format(features[i]))
if __name__ == "__main__":
try: