Merge remote-tracking branch 'Tasmota/development' into development

This commit is contained in:
Jason2866 2020-05-24 13:17:25 +02:00
commit 767f4d3d7b
32 changed files with 733 additions and 638 deletions

View File

@ -55,10 +55,13 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
### Version 8.3.1.2
- Change IRremoteESP8266 library updated to v2.7.7
- Change Adafruit_SGP30 library from v1.0.3 to v1.2.0 (#8519)
- Change Energy JSON Total field from ``"Total":[33.736,11.717,16.978]`` to ``"Total":33.736,"TotalTariff":[11.717,16.978]``
- Change Energy JSON ExportActive field from ``"ExportActive":[33.736,11.717,16.978]`` to ``"ExportActive":33.736,"ExportTariff":[11.717,16.978]``
- Add command ``Rule0`` to change global rule parameters
- Add commands ``LedPwmOn 0..255``, ``LedPwmOff 0..255`` and ``LedPwmMode1 0/1`` to control led brightness by George (#8491)
- Add more functionality to ``Switchmode`` 11 and 12 (#8450)
- Add wildcard pattern ``?`` for JSON matching in rules
- Add support for VEML6075 UVA/UVB/UVINDEX Sensor by device111 (#8432)
- Add support for VEML7700 Ambient light intensity Sensor by device111 (#8432)
- Add Three Phase Export Active Energy to SDM630 driver

View File

@ -1,46 +0,0 @@
Thank you for opening an issue on an Adafruit Arduino library repository. To
improve the speed of resolution please review the following guidelines and
common troubleshooting steps below before creating the issue:
- **Do not use GitHub issues for troubleshooting projects and issues.** Instead use
the forums at http://forums.adafruit.com to ask questions and troubleshoot why
something isn't working as expected. In many cases the problem is a common issue
that you will more quickly receive help from the forum community. GitHub issues
are meant for known defects in the code. If you don't know if there is a defect
in the code then start with troubleshooting on the forum first.
- **If following a tutorial or guide be sure you didn't miss a step.** Carefully
check all of the steps and commands to run have been followed. Consult the
forum if you're unsure or have questions about steps in a guide/tutorial.
- **For Arduino projects check these very common issues to ensure they don't apply**:
- For uploading sketches or communicating with the board make sure you're using
a **USB data cable** and **not** a **USB charge-only cable**. It is sometimes
very hard to tell the difference between a data and charge cable! Try using the
cable with other devices or swapping to another cable to confirm it is not
the problem.
- **Be sure you are supplying adequate power to the board.** Check the specs of
your board and plug in an external power supply. In many cases just
plugging a board into your computer is not enough to power it and other
peripherals.
- **Double check all soldering joints and connections.** Flakey connections
cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints.
- **Ensure you are using an official Arduino or Adafruit board.** We can't
guarantee a clone board will have the same functionality and work as expected
with this code and don't support them.
If you're sure this issue is a defect in the code and checked the steps above
please fill in the following fields to provide enough troubleshooting information.
You may delete the guideline and text above to just leave the following details:
- Arduino board: **INSERT ARDUINO BOARD NAME/TYPE HERE**
- Arduino IDE version (found in Arduino -> About Arduino menu): **INSERT ARDUINO
VERSION HERE**
- List the steps to reproduce the problem below (if possible attach a sketch or
copy the sketch code in too): **LIST REPRO STEPS BELOW**

View File

@ -1,26 +0,0 @@
Thank you for creating a pull request to contribute to Adafruit's GitHub code!
Before you open the request please review the following guidelines and tips to
help it be more easily integrated:
- **Describe the scope of your change--i.e. what the change does and what parts
of the code were modified.** This will help us understand any risks of integrating
the code.
- **Describe any known limitations with your change.** For example if the change
doesn't apply to a supported platform of the library please mention it.
- **Please run any tests or examples that can exercise your modified code.** We
strive to not break users of the code and running tests/examples helps with this
process.
Thank you again for contributing! We will try to test and integrate the change
as soon as we can, but be aware we have many GitHub repositories to manage and
can't immediately respond to every request. There is no need to bump or check in
on a pull request (it will clutter the discussion of the request).
Also don't be worried if the request is closed or not integrated--sometimes the
priorities of Adafruit's GitHub code (education, ease of use) might not match the
priorities of the pull request. Don't fret, the open source community thrives on
forks and GitHub makes it easy to keep your changes in a forked repo.
After reviewing the guidelines above you can delete this text from the pull request.

View File

@ -1,4 +0,0 @@
*~
Doxyfile*
doxygen_sqlite3.db
html

View File

@ -1,267 +0,0 @@
/*!
* @file Adafruit_SGP30.cpp
*
* @mainpage Adafruit SGP30 gas sensor driver
*
* @section intro_sec Introduction
*
* This is the documentation for Adafruit's SGP30 driver for the
* Arduino platform. It is designed specifically to work with the
* Adafruit SGP30 breakout: http://www.adafruit.com/products/3709
*
* These sensors use I2C to communicate, 2 pins (SCL+SDA) are required
* to interface with the breakout.
*
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
*
*
* @section author Author
* Written by Ladyada for Adafruit Industries.
*
* @section license License
* BSD license, all text here must be included in any redistribution.
*
*/
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "Adafruit_SGP30.h"
//#define I2C_DEBUG
/**************************************************************************/
/*!
@brief Instantiates a new SGP30 class
*/
/**************************************************************************/
Adafruit_SGP30::Adafruit_SGP30() {
}
/**************************************************************************/
/*!
@brief Setups the hardware and detects a valid SGP30. Initializes I2C
then reads the serialnumber and checks that we are talking to an SGP30
@param theWire Optional pointer to I2C interface, otherwise use Wire
@returns True if SGP30 found on I2C, False if something went wrong!
*/
/**************************************************************************/
boolean Adafruit_SGP30::begin(TwoWire *theWire) {
_i2caddr = SGP30_I2CADDR_DEFAULT;
if (theWire == NULL) {
_i2c = &Wire;
} else {
_i2c = theWire;
}
// assume i2c initialized already to avoid resetting clock stretching
// _i2c->begin();
uint8_t command[2];
command[0] = 0x36;
command[1] = 0x82;
if (! readWordFromCommand(command, 2, 10, serialnumber, 3))
return false;
uint16_t featureset;
command[0] = 0x20;
command[1] = 0x2F;
if (! readWordFromCommand(command, 2, 10, &featureset, 1))
return false;
//Serial.print("Featureset 0x"); Serial.println(featureset, HEX);
if (featureset != SGP30_FEATURESET)
return false;
if (! IAQinit())
return false;
return true;
}
/**************************************************************************/
/*!
@brief Commands the sensor to begin the IAQ algorithm. Must be called after startup.
@returns True if command completed successfully, false if something went wrong!
*/
/**************************************************************************/
boolean Adafruit_SGP30::IAQinit(void) {
uint8_t command[2];
command[0] = 0x20;
command[1] = 0x03;
return readWordFromCommand(command, 2, 10);
}
/**************************************************************************/
/*!
@brief Commands the sensor to take a single eCO2/VOC measurement. Places results in {@link TVOC} and {@link eCO2}
@returns True if command completed successfully, false if something went wrong!
*/
/**************************************************************************/
boolean Adafruit_SGP30::IAQmeasure(void) {
uint8_t command[2];
command[0] = 0x20;
command[1] = 0x08;
uint16_t reply[2];
if (! readWordFromCommand(command, 2, 12, reply, 2))
return false;
TVOC = reply[1];
eCO2 = reply[0];
return true;
}
/**************************************************************************/
/*!
@brief Request baseline calibration values for both CO2 and TVOC IAQ calculations. Places results in parameter memory locaitons.
@param eco2_base A pointer to a uint16_t which we will save the calibration value to
@param tvoc_base A pointer to a uint16_t which we will save the calibration value to
@returns True if command completed successfully, false if something went wrong!
*/
/**************************************************************************/
boolean Adafruit_SGP30::getIAQBaseline(uint16_t *eco2_base, uint16_t *tvoc_base) {
uint8_t command[2];
command[0] = 0x20;
command[1] = 0x15;
uint16_t reply[2];
if (! readWordFromCommand(command, 2, 10, reply, 2))
return false;
*eco2_base = reply[0];
*tvoc_base = reply[1];
return true;
}
/**************************************************************************/
/*!
@brief Assign baseline calibration values for both CO2 and TVOC IAQ calculations.
@param eco2_base A uint16_t which we will save the calibration value from
@param tvoc_base A uint16_t which we will save the calibration value from
@returns True if command completed successfully, false if something went wrong!
*/
/**************************************************************************/
boolean Adafruit_SGP30::setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base) {
uint8_t command[8];
command[0] = 0x20;
command[1] = 0x1e;
command[2] = tvoc_base >> 8;
command[3] = tvoc_base & 0xFF;
command[4] = generateCRC(command+2, 2);
command[5] = eco2_base >> 8;
command[6] = eco2_base & 0xFF;
command[7] = generateCRC(command+5, 2);
return readWordFromCommand(command, 8, 10);
}
/**************************************************************************/
/*!
@brief Set the absolute humidity value [mg/m^3] for compensation to increase precision of TVOC and eCO2.
@param absolute_humidity A uint32_t [mg/m^3] which we will be used for compensation. If the absolute humidity is set to zero, humidity compensation will be disabled.
@returns True if command completed successfully, false if something went wrong!
*/
/**************************************************************************/
boolean Adafruit_SGP30::setHumidity(uint32_t absolute_humidity) {
if (absolute_humidity > 256000) {
return false;
}
uint16_t ah_scaled = (uint16_t)(((uint64_t)absolute_humidity * 256 * 16777) >> 24);
uint8_t command[5];
command[0] = 0x20;
command[1] = 0x61;
command[2] = ah_scaled >> 8;
command[3] = ah_scaled & 0xFF;
command[4] = generateCRC(command+2, 2);
return readWordFromCommand(command, 5, 10);
}
/**************************************************************************/
/*!
@brief I2C low level interfacing
*/
/**************************************************************************/
boolean Adafruit_SGP30::readWordFromCommand(uint8_t command[], uint8_t commandLength, uint16_t delayms, uint16_t *readdata, uint8_t readlen)
{
uint8_t data;
_i2c->beginTransmission(_i2caddr);
#ifdef I2C_DEBUG
Serial.print("\t\t-> ");
#endif
for (uint8_t i=0; i<commandLength; i++) {
_i2c->write(command[i]);
#ifdef I2C_DEBUG
Serial.print("0x"); Serial.print(command[i], HEX); Serial.print(", ");
#endif
}
#ifdef I2C_DEBUG
Serial.println();
#endif
_i2c->endTransmission();
delay(delayms);
if (readlen == 0)
return true;
uint8_t replylen = readlen * (SGP30_WORD_LEN +1);
if (_i2c->requestFrom(_i2caddr, replylen) != replylen)
return false;
uint8_t replybuffer[replylen];
#ifdef I2C_DEBUG
Serial.print("\t\t<- ");
#endif
for (uint8_t i=0; i<replylen; i++) {
replybuffer[i] = _i2c->read();
#ifdef I2C_DEBUG
Serial.print("0x"); Serial.print(replybuffer[i], HEX); Serial.print(", ");
#endif
}
#ifdef I2C_DEBUG
Serial.println();
#endif
for (uint8_t i=0; i<readlen; i++) {
uint8_t crc = generateCRC(replybuffer+i*3, 2);
#ifdef I2C_DEBUG
Serial.print("\t\tCRC calced: 0x"); Serial.print(crc, HEX);
Serial.print(" vs. 0x"); Serial.println(replybuffer[i * 3 + 2], HEX);
#endif
if (crc != replybuffer[i * 3 + 2])
return false;
// success! store it
readdata[i] = replybuffer[i*3];
readdata[i] <<= 8;
readdata[i] |= replybuffer[i*3 + 1];
#ifdef I2C_DEBUG
Serial.print("\t\tRead: 0x"); Serial.println(readdata[i], HEX);
#endif
}
return true;
}
uint8_t Adafruit_SGP30::generateCRC(uint8_t *data, uint8_t datalen) {
// calculates 8-Bit checksum with given polynomial
uint8_t crc = SGP30_CRC8_INIT;
for (uint8_t i=0; i<datalen; i++) {
crc ^= data[i];
for (uint8_t b=0; b<8; b++) {
if (crc & 0x80)
crc = (crc << 1) ^ SGP30_CRC8_POLYNOMIAL;
else
crc <<= 1;
}
}
return crc;
}

View File

@ -1,69 +0,0 @@
/*!
* @file Adafruit_SGP30.h
*
* This is the documentation for Adafruit's SGP30 driver for the
* Arduino platform. It is designed specifically to work with the
* Adafruit SGP30 breakout: http://www.adafruit.com/products/3709
*
* These sensors use I2C to communicate, 2 pins (SCL+SDA) are required
* to interface with the breakout.
*
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
*
* Written by Ladyada for Adafruit Industries.
*
* BSD license, all text here must be included in any redistribution.
*
*/
#include "Arduino.h"
#include <Wire.h>
// the i2c address
#define SGP30_I2CADDR_DEFAULT 0x58 ///< SGP30 has only one I2C address
// commands and constants
#define SGP30_FEATURESET 0x0020 ///< The required set for this library
#define SGP30_CRC8_POLYNOMIAL 0x31 ///< Seed for SGP30's CRC polynomial
#define SGP30_CRC8_INIT 0xFF ///< Init value for CRC
#define SGP30_WORD_LEN 2 ///< 2 bytes per word
/**************************************************************************/
/*! Class that stores state and functions for interacting with SGP30 Gas Sensor */
/**************************************************************************/
class Adafruit_SGP30 {
public:
Adafruit_SGP30();
boolean begin(TwoWire *theWire = NULL);
boolean IAQinit(void);
boolean IAQmeasure(void);
boolean getIAQBaseline(uint16_t *eco2_base, uint16_t *tvoc_base);
boolean setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base);
boolean setHumidity(uint32_t absolute_humidity);
/**
* The last measurement of the IAQ-calculated Total Volatile Organic Compounds in ppb. This value is set when you call {@link IAQmeasure()}
*/
uint16_t TVOC;
/**
* The last measurement of the IAQ-calculated equivalent CO2 in ppm. This value is set when you call {@link IAQmeasure()}
*/
uint16_t eCO2;
/**
* The 48-bit serial number, this value is set when you call {@link begin()}
*/
uint16_t serialnumber[3];
private:
TwoWire *_i2c;
uint8_t _i2caddr;
void write(uint8_t address, uint8_t *data, uint8_t n);
void read(uint8_t address, uint8_t *data, uint8_t n);
boolean readWordFromCommand(uint8_t command[], uint8_t commandLength, uint16_t delay, uint16_t *readdata = NULL, uint8_t readlen = 0);
uint8_t generateCRC(uint8_t data[], uint8_t datalen);
};

View File

@ -1,18 +0,0 @@
Adafruit_SGP30
================
This is the Adafruit SGP30 Gas / Air Quality I2C sensor library
Tested and works great with the Aadafruit SGP30 Breakout Board
* http://www.adafruit.com/products/3709
This chip uses I2C to communicate, 2 pins are required to interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried for Adafruit Industries.
BSD license, check license.txt for more information
All text above must be included in any redistribution

View File

@ -0,0 +1,306 @@
/*!
* @file Adafruit_SGP30.cpp
*
* @mainpage Adafruit SGP30 gas sensor driver
*
* @section intro_sec Introduction
*
* This is the documentation for Adafruit's SGP30 driver for the
* Arduino platform. It is designed specifically to work with the
* Adafruit SGP30 breakout: http://www.adafruit.com/products/3709
*
* These sensors use I2C to communicate, 2 pins (SCL+SDA) are required
* to interface with the breakout.
*
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
*
*
* @section author Author
* Written by Ladyada for Adafruit Industries.
*
* @section license License
* BSD license, all text here must be included in any redistribution.
*
*/
#include "Arduino.h"
#include "Adafruit_SGP30.h"
//#define I2C_DEBUG
/*!
* @brief Instantiates a new SGP30 class
*/
Adafruit_SGP30::Adafruit_SGP30() {}
/*!
* @brief Setups the hardware and detects a valid SGP30. Initializes I2C
* then reads the serialnumber and checks that we are talking to an
* SGP30
* @param theWire
* Optional pointer to I2C interface, otherwise use Wire
* @param initSensor
* Optional pointer to prevent IAQinit to be called. Used for Deep
* Sleep.
* @return True if SGP30 found on I2C, False if something went wrong!
*/
boolean Adafruit_SGP30::begin(TwoWire *theWire, boolean initSensor) {
_i2caddr = SGP30_I2CADDR_DEFAULT;
_i2c = theWire;
_i2c->begin();
uint8_t command[2];
command[0] = 0x36;
command[1] = 0x82;
if (!readWordFromCommand(command, 2, 10, serialnumber, 3))
return false;
uint16_t featureset;
command[0] = 0x20;
command[1] = 0x2F;
if (!readWordFromCommand(command, 2, 10, &featureset, 1))
return false;
// Serial.print("Featureset 0x"); Serial.println(featureset, HEX);
if ((featureset & 0xF0) != SGP30_FEATURESET)
return false;
if (initSensor) {
if (!IAQinit())
return false;
}
return true;
}
/*!
* @brief Commands the sensor to perform a soft reset using the "General
* Call" mode. Take note that this is not sensor specific and all devices that
* support the General Call mode on the on the same I2C bus will perform this.
*
* @return True if command completed successfully, false if something went
* wrong!
*/
boolean Adafruit_SGP30::softReset(void) {
uint8_t command[2];
command[0] = 0x00;
command[1] = 0x06;
return readWordFromCommand(command, 2, 10);
}
/*!
* @brief Commands the sensor to begin the IAQ algorithm. Must be called
* after startup.
* @returns True if command completed successfully, false if something went
* wrong!
*/
boolean Adafruit_SGP30::IAQinit(void) {
uint8_t command[2];
command[0] = 0x20;
command[1] = 0x03;
return readWordFromCommand(command, 2, 10);
}
/*!
* @brief Commands the sensor to take a single eCO2/VOC measurement. Places
* results in {@link TVOC} and {@link eCO2}
* @return True if command completed successfully, false if something went
* wrong!
*/
boolean Adafruit_SGP30::IAQmeasure(void) {
uint8_t command[2];
command[0] = 0x20;
command[1] = 0x08;
uint16_t reply[2];
if (!readWordFromCommand(command, 2, 12, reply, 2))
return false;
TVOC = reply[1];
eCO2 = reply[0];
return true;
}
/*!
* @brief Commands the sensor to take a single H2/ethanol raw measurement.
* Places results in {@link rawH2} and {@link rawEthanol}
* @returns True if command completed successfully, false if something went
* wrong!
*/
boolean Adafruit_SGP30::IAQmeasureRaw(void) {
uint8_t command[2];
command[0] = 0x20;
command[1] = 0x50;
uint16_t reply[2];
if (!readWordFromCommand(command, 2, 25, reply, 2))
return false;
rawEthanol = reply[1];
rawH2 = reply[0];
return true;
}
/*!
* @brief Request baseline calibration values for both CO2 and TVOC IAQ
* calculations. Places results in parameter memory locaitons.
* @param eco2_base
* A pointer to a uint16_t which we will save the calibration
* value to
* @param tvoc_base
* A pointer to a uint16_t which we will save the calibration value to
* @return True if command completed successfully, false if something went
* wrong!
*/
boolean Adafruit_SGP30::getIAQBaseline(uint16_t *eco2_base,
uint16_t *tvoc_base) {
uint8_t command[2];
command[0] = 0x20;
command[1] = 0x15;
uint16_t reply[2];
if (!readWordFromCommand(command, 2, 10, reply, 2))
return false;
*eco2_base = reply[0];
*tvoc_base = reply[1];
return true;
}
/*!
* @brief Assign baseline calibration values for both CO2 and TVOC IAQ
* calculations.
* @param eco2_base
* A uint16_t which we will save the calibration value from
* @param tvoc_base
* A uint16_t which we will save the calibration value from
* @return True if command completed successfully, false if something went
* wrong!
*/
boolean Adafruit_SGP30::setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base) {
uint8_t command[8];
command[0] = 0x20;
command[1] = 0x1e;
command[2] = tvoc_base >> 8;
command[3] = tvoc_base & 0xFF;
command[4] = generateCRC(command + 2, 2);
command[5] = eco2_base >> 8;
command[6] = eco2_base & 0xFF;
command[7] = generateCRC(command + 5, 2);
return readWordFromCommand(command, 8, 10);
}
/*!
* @brief Set the absolute humidity value [mg/m^3] for compensation to
* increase precision of TVOC and eCO2.
* @param absolute_humidity
* A uint32_t [mg/m^3] which we will be used for compensation.
* If the absolute humidity is set to zero, humidity compensation
* will be disabled.
* @return True if command completed successfully, false if something went
* wrong!
*/
boolean Adafruit_SGP30::setHumidity(uint32_t absolute_humidity) {
if (absolute_humidity > 256000) {
return false;
}
uint16_t ah_scaled =
(uint16_t)(((uint64_t)absolute_humidity * 256 * 16777) >> 24);
uint8_t command[5];
command[0] = 0x20;
command[1] = 0x61;
command[2] = ah_scaled >> 8;
command[3] = ah_scaled & 0xFF;
command[4] = generateCRC(command + 2, 2);
return readWordFromCommand(command, 5, 10);
}
/*!
* @brief I2C low level interfacing
*/
boolean Adafruit_SGP30::readWordFromCommand(uint8_t command[],
uint8_t commandLength,
uint16_t delayms,
uint16_t *readdata,
uint8_t readlen) {
_i2c->beginTransmission(_i2caddr);
#ifdef I2C_DEBUG
Serial.print("\t\t-> ");
#endif
for (uint8_t i = 0; i < commandLength; i++) {
_i2c->write(command[i]);
#ifdef I2C_DEBUG
Serial.print("0x");
Serial.print(command[i], HEX);
Serial.print(", ");
#endif
}
#ifdef I2C_DEBUG
Serial.println();
#endif
_i2c->endTransmission();
delay(delayms);
if (readlen == 0)
return true;
uint8_t replylen = readlen * (SGP30_WORD_LEN + 1);
if (_i2c->requestFrom(_i2caddr, replylen) != replylen)
return false;
uint8_t replybuffer[replylen];
#ifdef I2C_DEBUG
Serial.print("\t\t<- ");
#endif
for (uint8_t i = 0; i < replylen; i++) {
replybuffer[i] = _i2c->read();
#ifdef I2C_DEBUG
Serial.print("0x");
Serial.print(replybuffer[i], HEX);
Serial.print(", ");
#endif
}
#ifdef I2C_DEBUG
Serial.println();
#endif
for (uint8_t i = 0; i < readlen; i++) {
uint8_t crc = generateCRC(replybuffer + i * 3, 2);
#ifdef I2C_DEBUG
Serial.print("\t\tCRC calced: 0x");
Serial.print(crc, HEX);
Serial.print(" vs. 0x");
Serial.println(replybuffer[i * 3 + 2], HEX);
#endif
if (crc != replybuffer[i * 3 + 2])
return false;
// success! store it
readdata[i] = replybuffer[i * 3];
readdata[i] <<= 8;
readdata[i] |= replybuffer[i * 3 + 1];
#ifdef I2C_DEBUG
Serial.print("\t\tRead: 0x");
Serial.println(readdata[i], HEX);
#endif
}
return true;
}
uint8_t Adafruit_SGP30::generateCRC(uint8_t *data, uint8_t datalen) {
// calculates 8-Bit checksum with given polynomial
uint8_t crc = SGP30_CRC8_INIT;
for (uint8_t i = 0; i < datalen; i++) {
crc ^= data[i];
for (uint8_t b = 0; b < 8; b++) {
if (crc & 0x80)
crc = (crc << 1) ^ SGP30_CRC8_POLYNOMIAL;
else
crc <<= 1;
}
}
return crc;
}

View File

@ -0,0 +1,80 @@
/*!
* @file Adafruit_SGP30.h
*
* This is the documentation for Adafruit's SGP30 driver for the
* Arduino platform. It is designed specifically to work with the
* Adafruit SGP30 breakout: http://www.adafruit.com/products/3709
*
* These sensors use I2C to communicate, 2 pins (SCL+SDA) are required
* to interface with the breakout.
*
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
*
* Written by Ladyada for Adafruit Industries.
*
* BSD license, all text here must be included in any redistribution.
*
*/
#include "Arduino.h"
#include <Wire.h>
// the i2c address
#define SGP30_I2CADDR_DEFAULT 0x58 ///< SGP30 has only one I2C address
// commands and constants
#define SGP30_FEATURESET 0x0020 ///< The required set for this library
#define SGP30_CRC8_POLYNOMIAL 0x31 ///< Seed for SGP30's CRC polynomial
#define SGP30_CRC8_INIT 0xFF ///< Init value for CRC
#define SGP30_WORD_LEN 2 ///< 2 bytes per word
/*!
* @brief Class that stores state and functions for interacting with
* SGP30 Gas Sensor
*/
class Adafruit_SGP30 {
public:
Adafruit_SGP30();
boolean begin(TwoWire *theWire = &Wire, boolean initSensor = true);
boolean softReset();
boolean IAQinit();
boolean IAQmeasure();
boolean IAQmeasureRaw();
boolean getIAQBaseline(uint16_t *eco2_base, uint16_t *tvoc_base);
boolean setIAQBaseline(uint16_t eco2_base, uint16_t tvoc_base);
boolean setHumidity(uint32_t absolute_humidity);
/** The last measurement of the IAQ-calculated Total Volatile Organic
* Compounds in ppb. This value is set when you call {@link IAQmeasure()} **/
uint16_t TVOC;
/** The last measurement of the IAQ-calculated equivalent CO2 in ppm. This
* value is set when you call {@link IAQmeasure()} **/
uint16_t eCO2;
/** The last measurement of the IAQ-calculated equivalent CO2 in ppm. This
* value is set when you call {@link IAQmeasureRaw()} **/
uint16_t rawH2;
/** The last measurement of the IAQ-calculated equivalent CO2 in ppm. This
* value is set when you call {@link IAQmeasureRaw()} **/
uint16_t rawEthanol;
/** The 48-bit serial number, this value is set when you call {@link begin()}
* **/
uint16_t serialnumber[3];
private:
TwoWire *_i2c;
uint8_t _i2caddr;
void write(uint8_t address, uint8_t *data, uint8_t n);
void read(uint8_t address, uint8_t *data, uint8_t n);
boolean readWordFromCommand(uint8_t command[], uint8_t commandLength,
uint16_t delay, uint16_t *readdata = NULL,
uint8_t readlen = 0);
uint8_t generateCRC(uint8_t data[], uint8_t datalen);
};

View File

@ -0,0 +1,54 @@
# Adafruit SGP30 Gas / Air Quality I2C sensor [[![Build Status](https://github.com/adafruit/Adafruit_SGP30/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit_SGP30/actions)[![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](http://adafruit.github.io/Adafruit_SGP30/html/index.html)
<a href="https://www.adafruit.com/product/3709"><img src="assets/board.jpg?raw=true" width="500px"></a>
This is the Adafruit SGP30 Gas / Air Quality I2C sensor library
Tested and works great with the Aadafruit SGP30 Breakout Board
* http://www.adafruit.com/products/3709
This chip uses I2C to communicate, 2 pins are required to interface
Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
# Installation
To install, use the Arduino Library Manager and search for "Adafruit SGP30" and install the library.
## Dependencies
* [Adafruit ILI9341](https://github.com/adafruit/Adafruit_ILI9341)
* [Adafruit GFX Library](https://github.com/adafruit/Adafruit-GFX-Library)
# Contributing
Contributions are welcome! Please read our [Code of Conduct](https://github.com/adafruit/Adafruit_SGP30/blob/master/CODE_OF_CONDUCT.md>)
before contributing to help this project stay welcoming.
## Documentation and doxygen
Documentation is produced by doxygen. Contributions should include documentation for any new code added.
Some examples of how to use doxygen can be found in these guide pages:
https://learn.adafruit.com/the-well-automated-arduino-library/doxygen
https://learn.adafruit.com/the-well-automated-arduino-library/doxygen-tips
## Formatting and clang-format
This library uses [`clang-format`](https://releases.llvm.org/download.html) to standardize the formatting of `.cpp` and `.h` files.
Contributions should be formatted using `clang-format`:
The `-i` flag will make the changes to the file.
```bash
clang-format -i *.cpp *.h
```
If you prefer to make the changes yourself, running `clang-format` without the `-i` flag will print out a formatted version of the file. You can save this to a file and diff it against the original to see the changes.
Note that the formatting output by `clang-format` is what the automated formatting checker will expect. Any diffs from this formatting will result in a failed build until they are addressed. Using the `-i` flag is highly recommended.
### clang-format resources
* [Binary builds and source available on the LLVM downloads page](https://releases.llvm.org/download.html)
* [Documentation and IDE integration](https://clang.llvm.org/docs/ClangFormat.html)
## About this Driver
Written by Limor Fried for Adafruit Industries.
BSD license, check license.txt for more information
All text above must be included in any redistribution

View File

@ -44,6 +44,14 @@ void loop() {
}
Serial.print("TVOC "); Serial.print(sgp.TVOC); Serial.print(" ppb\t");
Serial.print("eCO2 "); Serial.print(sgp.eCO2); Serial.println(" ppm");
if (! sgp.IAQmeasureRaw()) {
Serial.println("Raw Measurement failed");
return;
}
Serial.print("Raw H2 "); Serial.print(sgp.rawH2); Serial.print(" \t");
Serial.print("Raw Ethanol "); Serial.print(sgp.rawEthanol); Serial.println("");
delay(1000);
counter++;

View File

@ -1,5 +1,5 @@
name=Adafruit SGP30 Sensor
version=1.0.3
version=1.2.0
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=This is an Arduino library for the Adafruit SGP30 Gas / Air Quality Sensor
@ -7,3 +7,4 @@ paragraph=This is an Arduino library for the Adafruit SGP30 Gas / Air Quality Se
category=Sensors
url=https://github.com/adafruit/Adafruit_SGP30
architectures=*
depends=Adafruit ILI9341, Adafruit GFX Library

View File

@ -76,6 +76,29 @@ boolean Adafruit_VEML7700::begin(TwoWire *theWire) {
return true;
}
float Adafruit_VEML7700::alternate_pow(float a, float b)
{
// https://martin.ankerl.com/2012/01/25/optimized-approximative-pow-in-c-and-cpp/
// calculate approximation with fraction of the exponent
int e = abs((int)b);
union {
double d;
int x[2];
} u = { a };
u.x[1] = (int)((b - e) * (u.x[1] - 1072632447) + 1072632447);
u.x[0] = 0;
// exponentiation by squaring with the exponent's integer part
// double r = u.d makes everything much slower, not sure why
double r = 1.0;
while (e) {
if (e & 1) {
r *= a;
}
a *= a;
e >>= 1;
}
return r * u.d;
}
float Adafruit_VEML7700::normalize_resolution(float value) {
// adjust for gain (1x is normalized)
@ -123,7 +146,7 @@ float Adafruit_VEML7700::readLuxNormalized() {
// user-provided correction for non-linearities at high lux/white values:
// https://forums.adafruit.com/viewtopic.php?f=19&t=152997&p=758582#p759346
if ((getGain() == VEML7700_GAIN_1_8) && (getIntegrationTime() == VEML7700_IT_25MS)){
lux = 6.0135e-13*pow(lux,4) - 9.3924e-9*pow(lux,3) + 8.1488e-5*pow(lux,2) + 1.0023*lux;
lux = 6.0135e-13*alternate_pow(lux,4) - 9.3924e-9*alternate_pow(lux,3) + 8.1488e-5*alternate_pow(lux,2) + 1.0023*lux;
}
return lux;
@ -156,7 +179,7 @@ float Adafruit_VEML7700::readWhiteNormalized() {
// user-provided correction for non-linearities at high lux values:
// https://forums.adafruit.com/viewtopic.php?f=19&t=152997&p=758582#p759346
if ((getGain() == VEML7700_GAIN_1_8) && (getIntegrationTime() == VEML7700_IT_25MS)){
white = 2E-15*pow(white,4) + 4E-12*pow(white,3) + 9E-06*pow(white,2) + 1.0179*white - 11.052;
white = 2E-15*alternate_pow(white,4) + 4E-12*alternate_pow(white,3) + 9E-06*alternate_pow(white,2) + 1.0179*white - 11.052;
}
return white;

View File

@ -14,6 +14,11 @@
* BSD license (see license.txt)
*/
/*
* change from device111 for Tasmota
* Add alternativ Pow function for readLuxNormalized() and readWhiteNormalized()
*/
#ifndef _ADAFRUIT_VEML7700_H
#define _ADAFRUIT_VEML7700_H
@ -105,6 +110,7 @@ private:
*PowerSave_Enable, *PowerSave_Mode;
float normalize_resolution(float value);
float alternate_pow(float a, float b);
Adafruit_I2CDevice *i2c_dev;

View File

@ -85,7 +85,7 @@ extra_scripts = ${scripts_defaults.extra_scripts}
[tasmota_stage]
; *** Esp8266 core for Arduino version Tasmota stage
extends = tasmota_core
platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#a5432625d93f60d7e28cfdc5ed8abb3e0151951d
platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#52b3e5b7b3ccedcede665682f7896b637b64dbf5
; *********** Alternative Options, enable only if you know exactly what you do ********
; NONOSDK221
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221

View File

@ -4,7 +4,10 @@
- Change Energy JSON Total field from ``"Total":[33.736,11.717,16.978]`` to ``"Total":33.736,"TotalTariff":[11.717,16.978]``
- Change Energy JSON ExportActive field from ``"ExportActive":[33.736,11.717,16.978]`` to ``"ExportActive":33.736,"ExportTariff":[11.717,16.978]``
- Change Adafruit_SGP30 library from v1.0.3 to v1.2.0 (#8519)
- Add Three Phase Export Active Energy to SDM630 driver
- Add commands ``LedPwmOn 0..255``, ``LedPwmOff 0..255`` and ``LedPwmMode1 0/1`` to control led brightness by George (#8491)
- Add wildcard pattern ``?`` for JSON matching in rules
### 8.3.1.1 20200518

View File

@ -295,9 +295,9 @@
#define D_CMND_LEDPOWER "LedPower"
#define D_CMND_LEDSTATE "LedState"
#define D_CMND_LEDMASK "LedMask"
#define D_CMND_LEDPWMOFF "LedPwmOff"
#define D_CMND_LEDPWMON "LedPwmOn"
#define D_CMND_LEDPWMMODE "LedPwmMode"
#define D_CMND_LEDPWM_OFF "LedPwmOff"
#define D_CMND_LEDPWM_ON "LedPwmOn"
#define D_CMND_LEDPWM_MODE "LedPwmMode"
#define D_CMND_WIFIPOWER "WifiPower"
#define D_CMND_SPEEDUNIT "SpeedUnit"
#define D_CMND_I2CSCAN "I2CScan"

View File

@ -1874,65 +1874,6 @@ void AddLogBufferSize(uint32_t loglevel, uint8_t *buffer, uint32_t count, uint32
AddLog(loglevel);
}
/*********************************************************************************************\
* JSON parsing
\*********************************************************************************************/
// does the character needs to be escaped, and if so with which character
char escapeJSONChar(char c) {
if ((c == '\"') || (c == '\\')) {
return c;
}
if (c == '\n') { return 'n'; }
if (c == '\t') { return 't'; }
if (c == '\r') { return 'r'; }
if (c == '\f') { return 'f'; }
if (c == '\b') { return 'b'; }
return 0;
}
String escapeJSONString(const char *str) {
String r("");
if (nullptr == str) { return r; }
bool needs_escape = false;
size_t len_out = 1;
const char * c = str;
while (*c) {
if (escapeJSONChar(*c)) {
len_out++;
needs_escape = true;
}
c++;
len_out++;
}
if (needs_escape) {
// we need to escape some chars
// allocate target buffer
r.reserve(len_out);
c = str;
char *d = r.begin();
while (*c) {
char c2 = escapeJSONChar(*c);
if (c2) {
c++;
*d++ = '\\';
*d++ = c2;
} else {
*d++ = *c++;
}
}
*d = 0; // add NULL terminator
r = (char*) r.begin(); // assign the buffer to the string
} else {
r = str;
}
return r;
}
/*********************************************************************************************\
* Uncompress static PROGMEM strings
\*********************************************************************************************/

View File

@ -26,8 +26,8 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix
D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALCONFIG "|"
D_CMND_SERIALDELIMITER "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|"
D_CMND_DEVICENAME "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|"
D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" D_CMND_HUMOFFSET "|"
D_CMND_SPEEDUNIT "|" D_CMND_GLOBAL_TEMP "|" D_CMND_GLOBAL_HUM "|" D_CMND_LEDPWMON "|" D_CMND_LEDPWMOFF "|" D_CMND_LEDPWMMODE "|"
D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_LEDPWM_ON "|" D_CMND_LEDPWM_OFF "|" D_CMND_LEDPWM_MODE "|"
D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" D_CMND_HUMOFFSET "|" D_CMND_SPEEDUNIT "|" D_CMND_GLOBAL_TEMP "|" D_CMND_GLOBAL_HUM "|"
#ifdef USE_I2C
D_CMND_I2CSCAN "|" D_CMND_I2CDRIVER "|"
#endif
@ -49,8 +49,8 @@ void (* const TasmotaCommand[])(void) PROGMEM = {
&CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig,
&CmndSerialDelimiter, &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig,
&CmndDevicename, &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd,
&CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndWifiPower, &CmndTempOffset, &CmndHumOffset,
&CmndSpeedUnit, &CmndGlobalTemp, &CmndGlobalHum, &CmndLedPwmOn, &CmndLedPwmOff, &CmndLedPwmMode,
&CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndLedPwmOn, &CmndLedPwmOff, &CmndLedPwmMode,
&CmndWifiPower, &CmndTempOffset, &CmndHumOffset, &CmndSpeedUnit, &CmndGlobalTemp, &CmndGlobalHum,
#ifdef USE_I2C
&CmndI2cScan, CmndI2cDriver,
#endif
@ -1804,6 +1804,62 @@ void CmndLedMask(void)
ResponseCmndChar(stemp1);
}
void CmndLedPwmOff(void)
{
if (XdrvMailbox.data_len > 0) {
if (XdrvMailbox.payload < 0) {
Settings.ledpwm_off = 0;
}
else if (XdrvMailbox.payload > 255) {
Settings.ledpwm_off = 255;
} else {
Settings.ledpwm_off = XdrvMailbox.payload;
}
UpdateLedPowerAll();
}
ResponseCmndNumber(Settings.ledpwm_off);
}
void CmndLedPwmOn(void)
{
if (XdrvMailbox.data_len > 0) {
if (XdrvMailbox.payload < 0) {
Settings.ledpwm_on = 0;
}
else if (XdrvMailbox.payload > 255) {
Settings.ledpwm_on = 255;
} else {
Settings.ledpwm_on = XdrvMailbox.payload;
}
UpdateLedPowerAll();
}
ResponseCmndNumber(Settings.ledpwm_on);
}
void CmndLedPwmMode(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_LEDS)) {
if (!PinUsed(GPIO_LEDLNK)) { XdrvMailbox.index = 1; }
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) {
uint32_t mask = 1 << (XdrvMailbox.index -1); // Led to configure
switch (XdrvMailbox.payload) {
case 0: // digital
Settings.ledpwm_mask &= (0xFF ^ mask);
break;
case 1: // pwm
Settings.ledpwm_mask |= mask;
break;
case 2: // toggle
Settings.ledpwm_mask ^= mask;
break;
}
UpdateLedPowerAll();
}
bool state = bitRead(Settings.ledpwm_mask, XdrvMailbox.index -1);
ResponseCmndIdxChar(GetStateText(state));
}
}
void CmndWifiPower(void)
{
if (XdrvMailbox.data_len > 0) {
@ -1890,59 +1946,3 @@ void CmndDriver(void)
{
XdrvCall(FUNC_COMMAND_DRIVER);
}
void CmndLedPwmOff(void)
{
if (XdrvMailbox.data_len > 0) {
if (XdrvMailbox.payload < 0) {
Settings.ledpwm_off = 0;
}
else if (XdrvMailbox.payload > 255) {
Settings.ledpwm_off = 255;
} else {
Settings.ledpwm_off = XdrvMailbox.payload;
}
UpdateLedPowerAll();
}
ResponseCmndNumber(Settings.ledpwm_off);
}
void CmndLedPwmOn(void)
{
if (XdrvMailbox.data_len > 0) {
if (XdrvMailbox.payload < 0) {
Settings.ledpwm_on = 0;
}
else if (XdrvMailbox.payload > 255) {
Settings.ledpwm_on = 255;
} else {
Settings.ledpwm_on = XdrvMailbox.payload;
}
UpdateLedPowerAll();
}
ResponseCmndNumber(Settings.ledpwm_on);
}
void CmndLedPwmMode(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_LEDS)) {
if (!PinUsed(GPIO_LEDLNK)) { XdrvMailbox.index = 1; }
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) {
uint32_t mask = 1 << (XdrvMailbox.index -1); // Led to configure
switch (XdrvMailbox.payload) {
case 0: // digital
Settings.ledpwm_mask &= (0xFF ^ mask);
break;
case 1: // pwm
Settings.ledpwm_mask |= mask;
break;
case 2: // toggle
Settings.ledpwm_mask ^= mask;
break;
}
UpdateLedPowerAll();
}
bool state = bitRead(Settings.ledpwm_mask, XdrvMailbox.index -1);
ResponseCmndIdxChar(GetStateText(state));
}
}

106
tasmota/support_json.ino Normal file
View File

@ -0,0 +1,106 @@
/*
support_json.ino - JSON support functions
Copyright (C) 2020 Theo Arends and Stephan Hadinger
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/>.
*/
/*********************************************************************************************\
* JSON parsing
\*********************************************************************************************/
// does the character needs to be escaped, and if so with which character
char EscapeJSONChar(char c) {
if ((c == '\"') || (c == '\\')) {
return c;
}
if (c == '\n') { return 'n'; }
if (c == '\t') { return 't'; }
if (c == '\r') { return 'r'; }
if (c == '\f') { return 'f'; }
if (c == '\b') { return 'b'; }
return 0;
}
String EscapeJSONString(const char *str) {
String r("");
if (nullptr == str) { return r; }
bool needs_escape = false;
size_t len_out = 1;
const char * c = str;
while (*c) {
if (EscapeJSONChar(*c)) {
len_out++;
needs_escape = true;
}
c++;
len_out++;
}
if (needs_escape) {
// we need to escape some chars
// allocate target buffer
r.reserve(len_out);
c = str;
char *d = r.begin();
while (*c) {
char c2 = EscapeJSONChar(*c);
if (c2) {
c++;
*d++ = '\\';
*d++ = c2;
} else {
*d++ = *c++;
}
}
*d = 0; // add NULL terminator
r = (char*) r.begin(); // assign the buffer to the string
} else {
r = str;
}
return r;
}
/*********************************************************************************************\
* Find key - case insensitive
\*********************************************************************************************/
// Given a JsonObject, finds the value as JsonVariant for the key needle.
// The search is case-insensitive, and will find the first match in the order of keys in JSON
//
// If the key is not found, returns a nullptr
// Input: needle cannot be NULL but may be PROGMEM
const JsonVariant &GetCaseInsensitive(const JsonObject &json, const char *needle) {
// key can be in PROGMEM
// if needle == "?" then we return the first valid key
bool wildcard = strcmp_P("?", needle) == 0;
if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle))) {
return *(JsonVariant*)nullptr;
}
for (JsonObject::const_iterator it=json.begin(); it!=json.end(); ++it) {
const char *key = it->key;
const JsonVariant &value = it->value;
if (wildcard || (0 == strcasecmp_P(key, needle))) {
return value;
}
}
// if not found
return *(JsonVariant*)nullptr;
}

View File

@ -1291,16 +1291,23 @@ void SerialInput(void)
}
} else {
if (serial_in_byte || Settings.flag.mqtt_serial_raw) { // Any char between 1 and 127 or any char (0 - 255) - CMND_SERIALSEND3
bool in_byte_is_delimiter = // Char is delimiter when...
(((Settings.serial_delimiter < 128) && (serial_in_byte == Settings.serial_delimiter)) || // Any char between 1 and 127 and being delimiter
((Settings.serial_delimiter == 128) && !isprint(serial_in_byte))) && // Any char not between 32 and 127
!Settings.flag.mqtt_serial_raw; // In raw mode (CMND_SERIALSEND3) there is never a delimiter
if ((serial_in_byte_counter < INPUT_BUFFER_SIZE -1) && // Add char to string if it still fits and ...
((isprint(serial_in_byte) && (128 == Settings.serial_delimiter)) || // Any char between 32 and 127
((serial_in_byte != Settings.serial_delimiter) && (128 != Settings.serial_delimiter)) || // Any char between 1 and 127 and not being delimiter
Settings.flag.mqtt_serial_raw)) { // Any char between 0 and 255 - CMND_SERIALSEND3
!in_byte_is_delimiter) { // Char is not a delimiter
serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
serial_polling_window = millis();
} else {
}
if ((serial_in_byte_counter >= INPUT_BUFFER_SIZE -1) || // Send message when buffer is full or ...
in_byte_is_delimiter) { // Char is delimiter
serial_polling_window = 0; // Reception done - send mqtt
break;
}
serial_polling_window = millis(); // Wait for next char
}
}

View File

@ -54,17 +54,23 @@ void SerialBridgeInput(void)
return;
}
if (serial_in_byte || serial_bridge_raw) { // Any char between 1 and 127 or any char (0 - 255)
bool in_byte_is_delimiter = // Char is delimiter when...
(((Settings.serial_delimiter < 128) && (serial_in_byte == Settings.serial_delimiter)) || // Any char between 1 and 127 and being delimiter
((Settings.serial_delimiter == 128) && !isprint(serial_in_byte))) && // Any char not between 32 and 127
!serial_bridge_raw; // In raw mode (CMND_SERIALSEND3) there is never a delimiter
if ((serial_bridge_in_byte_counter < SERIAL_BRIDGE_BUFFER_SIZE -1) && // Add char to string if it still fits and ...
((isprint(serial_in_byte) && (128 == Settings.serial_delimiter)) || // Any char between 32 and 127
((serial_in_byte != Settings.serial_delimiter) && (128 != Settings.serial_delimiter)) || // Any char between 1 and 127 and not being delimiter
serial_bridge_raw)) { // Any char between 0 and 255
!in_byte_is_delimiter) { // Char is not a delimiter
serial_bridge_buffer[serial_bridge_in_byte_counter++] = serial_in_byte;
serial_bridge_polling_window = millis(); // Wait for more data
} else {
}
if ((serial_bridge_in_byte_counter >= SERIAL_BRIDGE_BUFFER_SIZE -1) || // Send message when buffer is full or ...
in_byte_is_delimiter) { // Char is delimiter
serial_bridge_polling_window = 0; // Publish now
break;
}
serial_bridge_polling_window = millis(); // Wait for more data
}
}

View File

@ -441,25 +441,20 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
break;
}
}
snprintf_P(stemp, sizeof(stemp), PSTR("%%TIME%%"));
if (rule_param.startsWith(stemp)) {
if (rule_param.startsWith(F("%TIME%"))) {
rule_param = String(MinutesPastMidnight());
}
snprintf_P(stemp, sizeof(stemp), PSTR("%%UPTIME%%"));
if (rule_param.startsWith(stemp)) {
if (rule_param.startsWith(F("%UPTIME%"))) {
rule_param = String(MinutesUptime());
}
snprintf_P(stemp, sizeof(stemp), PSTR("%%TIMESTAMP%%"));
if (rule_param.startsWith(stemp)) {
if (rule_param.startsWith(F("%TIMESTAMP%"))) {
rule_param = GetDateAndTime(DT_LOCAL).c_str();
}
#if defined(USE_TIMERS) && defined(USE_SUNRISE)
snprintf_P(stemp, sizeof(stemp), PSTR("%%SUNRISE%%"));
if (rule_param.startsWith(stemp)) {
if (rule_param.startsWith(F("%SUNRISE%"))) {
rule_param = String(SunMinutes(0));
}
snprintf_P(stemp, sizeof(stemp), PSTR("%%SUNSET%%"));
if (rule_param.startsWith(stemp)) {
if (rule_param.startsWith(F("%SUNSET%"))) {
rule_param = String(SunMinutes(1));
}
#endif // USE_TIMERS and USE_SUNRISE
@ -493,13 +488,17 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
uint32_t i = 0;
while ((pos = rule_name.indexOf("#")) > 0) { // "SUBTYPE1#SUBTYPE2#CURRENT"
subtype = rule_name.substring(0, pos);
if (!(*obj)[subtype].success()) { return false; } // No subtype in JSON data
JsonObject &obj2 = (*obj)[subtype];
obj = &obj2;
const JsonVariant & val = GetCaseInsensitive(*obj, subtype.c_str());
if (nullptr == &val) { return false; } // not found
obj = &(val.as<JsonObject>());
if (!obj->success()) { return false; } // not a JsonObject
rule_name = rule_name.substring(pos +1);
if (i++ > 10) { return false; } // Abandon possible loop
}
if (!(*obj)[rule_name].success()) { return false; } // No name in JSON data
const JsonVariant & val = GetCaseInsensitive(*obj, rule_name.c_str());
if (nullptr == &val) { return false; } // last level not found
const char* str_value;
if (rule_name_idx) {
str_value = (*obj)[rule_name][rule_name_idx -1]; // "CURRENT[1]"
@ -2036,7 +2035,7 @@ void CmndRule(void)
XdrvMailbox.command, index, GetStateText(bitRead(Settings.rule_enabled, index -1)), GetStateText(bitRead(Settings.rule_once, index -1)),
GetStateText(bitRead(Settings.rule_stop, index -1)),
rule_len, MAX_RULE_SIZE - GetRuleLenStorage(index - 1),
escapeJSONString(rule.c_str()).c_str());
EscapeJSONString(rule.c_str()).c_str());
}
}

View File

@ -437,8 +437,8 @@ void HueLightStatus2(uint8_t device, String *response)
fname[fname_len] = 0x00;
}
snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON2,
escapeJSONString(fname).c_str(),
escapeJSONString(Settings.user_template_name).c_str(),
EscapeJSONString(fname).c_str(),
EscapeJSONString(Settings.user_template_name).c_str(),
PSTR("Tasmota"),
GetHueDeviceId(device).c_str());
*response += buf;

View File

@ -23,29 +23,9 @@
void ZigbeeZCLSend_Raw(uint16_t dtsAddr, uint16_t groupaddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId);
// Get an JSON attribute, with case insensitive key search
const JsonVariant &getCaseInsensitive(const JsonObject &json, const char *needle) {
// key can be in PROGMEM
if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle))) {
return *(JsonVariant*)nullptr;
}
for (JsonObject::const_iterator it=json.begin(); it!=json.end(); ++it) {
const char *key = it->key;
const JsonVariant &value = it->value;
if (0 == strcasecmp_P(key, needle)) {
return value;
}
}
// if not found
return *(JsonVariant*)nullptr;
}
// get the result as a string (const char*) and nullptr if there is no field or the string is empty
const char * getCaseInsensitiveConstCharNull(const JsonObject &json, const char *needle) {
const JsonVariant &val = getCaseInsensitive(json, needle);
const JsonVariant &val = GetCaseInsensitive(json, needle);
if (&val) {
const char *val_cs = val.as<const char*>();
if (strlen(val_cs)) {

View File

@ -1075,7 +1075,7 @@ int32_t Z_Devices::deviceRestore(const JsonObject &json) {
size_t endpoints_len = 0;
// read mandatory "Device"
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR("Device"));
if (nullptr != &val_device) {
device = strToUInt(val_device);
} else {
@ -1083,7 +1083,7 @@ int32_t Z_Devices::deviceRestore(const JsonObject &json) {
}
// read "IEEEAddr" 64 bits in format "0x0000000000000000"
const JsonVariant &val_ieeeaddr = getCaseInsensitive(json, PSTR("IEEEAddr"));
const JsonVariant &val_ieeeaddr = GetCaseInsensitive(json, PSTR("IEEEAddr"));
if (nullptr != &val_ieeeaddr) {
ieeeaddr = strtoull(val_ieeeaddr.as<const char*>(), nullptr, 0);
}
@ -1098,7 +1098,7 @@ int32_t Z_Devices::deviceRestore(const JsonObject &json) {
manufid = getCaseInsensitiveConstCharNull(json, PSTR("Manufacturer"));
// read "Light"
const JsonVariant &val_bulbtype = getCaseInsensitive(json, PSTR(D_JSON_ZIGBEE_LIGHT));
const JsonVariant &val_bulbtype = GetCaseInsensitive(json, PSTR(D_JSON_ZIGBEE_LIGHT));
if (nullptr != &val_bulbtype) { bulbtype = strToUInt(val_bulbtype);; }
// update internal device information
@ -1109,7 +1109,7 @@ int32_t Z_Devices::deviceRestore(const JsonObject &json) {
if (&val_bulbtype) { setHueBulbtype(device, bulbtype); }
// read "Endpoints"
const JsonVariant &val_endpoints = getCaseInsensitive(json, PSTR("Endpoints"));
const JsonVariant &val_endpoints = GetCaseInsensitive(json, PSTR("Endpoints"));
if ((nullptr != &val_endpoints) && (val_endpoints.is<JsonArray>())) {
const JsonArray &arr_ep = val_endpoints.as<const JsonArray&>();
endpoints_len = arr_ep.size();

View File

@ -85,9 +85,9 @@ void HueLightStatus2Zigbee(uint16_t shortaddr, String *response)
snprintf_P(shortaddrname, sizeof(shortaddrname), PSTR("0x%04X"), shortaddr);
snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON2,
(friendlyName) ? escapeJSONString(friendlyName).c_str() : shortaddrname,
(modelId) ? escapeJSONString(modelId).c_str() : PSTR("Unknown"),
(manufacturerId) ? escapeJSONString(manufacturerId).c_str() : PSTR("Tasmota"),
(friendlyName) ? EscapeJSONString(friendlyName).c_str() : shortaddrname,
(modelId) ? EscapeJSONString(modelId).c_str() : PSTR("Unknown"),
(manufacturerId) ? EscapeJSONString(manufacturerId).c_str() : PSTR("Tasmota"),
GetHueDeviceId(shortaddr).c_str());
*response += buf;

View File

@ -600,7 +600,7 @@ void Z_SendAFInfoRequest(uint16_t shortaddr) {
void Z_AqaraOccupancy(uint16_t shortaddr, uint16_t cluster, uint8_t endpoint, const JsonObject &json) {
static const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; // 90 s
// Read OCCUPANCY value if any
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR(OCCUPANCY));
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR(OCCUPANCY));
if (nullptr != &val_endpoint) {
uint32_t occupancy = strToUInt(val_endpoint);

View File

@ -427,13 +427,13 @@ void CmndZbSend(void) {
bool clusterSpecific = true;
// parse JSON
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR("Device"));
if (nullptr != &val_device) {
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
if (BAD_SHORTADDR == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
}
if (BAD_SHORTADDR == device) { // if not found, check if we have a group
const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group"));
const JsonVariant &val_group = GetCaseInsensitive(json, PSTR("Group"));
if (nullptr != &val_group) {
groupaddr = strToUInt(val_group);
} else { // no device nor group
@ -442,11 +442,11 @@ void CmndZbSend(void) {
}
}
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint"));
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR("Endpoint"));
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
const JsonVariant &val_manuf = getCaseInsensitive(json, PSTR("Manuf"));
const JsonVariant &val_manuf = GetCaseInsensitive(json, PSTR("Manuf"));
if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); }
const JsonVariant &val_cmd = getCaseInsensitive(json, PSTR("Send"));
const JsonVariant &val_cmd = GetCaseInsensitive(json, PSTR("Send"));
if (nullptr != &val_cmd) {
// probe the type of the argument
// If JSON object, it's high level commands
@ -582,7 +582,7 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
// Information about source device: "Device", "Endpoint", "Cluster"
// - the source endpoint must have a known IEEE address
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR("Device"));
if (nullptr != &val_device) {
srcDevice = zigbee_devices.parseDeviceParam(val_device.as<char*>());
}
@ -591,17 +591,17 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(srcDevice);
if (0 == srcLongAddr) { ResponseCmndChar_P(PSTR("Unknown source IEEE address")); return; }
// look for source endpoint
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint"));
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR("Endpoint"));
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
// look for source cluster
const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster"));
const JsonVariant &val_cluster = GetCaseInsensitive(json, PSTR("Cluster"));
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
// Either Device address
// In this case the following parameters are mandatory
// - "ToDevice" and the device must have a known IEEE address
// - "ToEndpoint"
const JsonVariant &dst_device = getCaseInsensitive(json, PSTR("ToDevice"));
const JsonVariant &dst_device = GetCaseInsensitive(json, PSTR("ToDevice"));
if (nullptr != &dst_device) {
dstDevice = zigbee_devices.parseDeviceParam(dst_device.as<char*>());
if (BAD_SHORTADDR == dstDevice) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
@ -612,12 +612,12 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
}
if (0 == dstLongAddr) { ResponseCmndChar_P(PSTR("Unknown dest IEEE address")); return; }
const JsonVariant &val_toendpoint = getCaseInsensitive(json, PSTR("ToEndpoint"));
const JsonVariant &val_toendpoint = GetCaseInsensitive(json, PSTR("ToEndpoint"));
if (nullptr != &val_toendpoint) { toendpoint = strToUInt(val_endpoint); } else { toendpoint = endpoint; }
}
// Or Group Address - we don't need a dstEndpoint in this case
const JsonVariant &to_group = getCaseInsensitive(json, PSTR("ToGroup"));
const JsonVariant &to_group = GetCaseInsensitive(json, PSTR("ToGroup"));
if (nullptr != &to_group) { toGroup = strToUInt(to_group); }
// make sure we don't have conflicting parameters
@ -907,13 +907,13 @@ void CmndZbRead(void) {
size_t attrs_len = 0;
uint8_t* attrs = nullptr; // empty string is valid
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
const JsonVariant &val_device = GetCaseInsensitive(json, PSTR("Device"));
if (nullptr != &val_device) {
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
if (BAD_SHORTADDR == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
}
if (BAD_SHORTADDR == device) { // if not found, check if we have a group
const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group"));
const JsonVariant &val_group = GetCaseInsensitive(json, PSTR("Group"));
if (nullptr != &val_group) {
groupaddr = strToUInt(val_group);
} else { // no device nor group
@ -922,14 +922,14 @@ void CmndZbRead(void) {
}
}
const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster"));
const JsonVariant &val_cluster = GetCaseInsensitive(json, PSTR("Cluster"));
if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); }
const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint"));
const JsonVariant &val_endpoint = GetCaseInsensitive(json, PSTR("Endpoint"));
if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); }
const JsonVariant &val_manuf = getCaseInsensitive(json, PSTR("Manuf"));
const JsonVariant &val_manuf = GetCaseInsensitive(json, PSTR("Manuf"));
if (nullptr != &val_manuf) { manuf = strToUInt(val_manuf); }
const JsonVariant &val_attr = getCaseInsensitive(json, PSTR("Read"));
const JsonVariant &val_attr = GetCaseInsensitive(json, PSTR("Read"));
if (nullptr != &val_attr) {
uint16_t val = strToUInt(val_attr);
if (val_attr.is<JsonArray>()) {
@ -1034,21 +1034,21 @@ void CmndZbConfig(void) {
if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
// Channel
const JsonVariant &val_channel = getCaseInsensitive(json, PSTR("Channel"));
const JsonVariant &val_channel = GetCaseInsensitive(json, PSTR("Channel"));
if (nullptr != &val_channel) { zb_channel = strToUInt(val_channel); }
if (zb_channel < 11) { zb_channel = 11; }
if (zb_channel > 26) { zb_channel = 26; }
// PanID
const JsonVariant &val_pan_id = getCaseInsensitive(json, PSTR("PanID"));
const JsonVariant &val_pan_id = GetCaseInsensitive(json, PSTR("PanID"));
if (nullptr != &val_pan_id) { zb_pan_id = strToUInt(val_pan_id); }
// ExtPanID
const JsonVariant &val_ext_pan_id = getCaseInsensitive(json, PSTR("ExtPanID"));
const JsonVariant &val_ext_pan_id = GetCaseInsensitive(json, PSTR("ExtPanID"));
if (nullptr != &val_ext_pan_id) { zb_ext_panid = strtoull(val_ext_pan_id.as<const char*>(), nullptr, 0); }
// KeyL
const JsonVariant &val_key_l = getCaseInsensitive(json, PSTR("KeyL"));
const JsonVariant &val_key_l = GetCaseInsensitive(json, PSTR("KeyL"));
if (nullptr != &val_key_l) { zb_precfgkey_l = strtoull(val_key_l.as<const char*>(), nullptr, 0); }
// KeyH
const JsonVariant &val_key_h = getCaseInsensitive(json, PSTR("KeyH"));
const JsonVariant &val_key_h = GetCaseInsensitive(json, PSTR("KeyH"));
if (nullptr != &val_key_h) { zb_precfgkey_h = strtoull(val_key_h.as<const char*>(), nullptr, 0); }
// Check if a parameter was changed after all

View File

@ -119,11 +119,13 @@ void Sgp30Show(bool json)
if (sgp30_ready) {
char abs_hum[33];
if (global_update && global_humidity>0 && global_temperature!=9999) {
// has humidity + temperature
dtostrfd(sgp30_abshum,4,abs_hum);
}
if (json) {
ResponseAppend_P(PSTR(",\"SGP30\":{\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TVOC "\":%d"), sgp.eCO2, sgp.TVOC);
if (global_update && global_humidity>0 && global_temperature!=9999) {
// has humidity + temperature
dtostrfd(sgp30_abshum,4,abs_hum);
ResponseAppend_P(PSTR(",\"" D_JSON_AHUM "\":%s"),abs_hum);
}
ResponseJsonEnd();