From ca8b38d0d022c9a2fe50c0a338059717c2de68ec Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 23 May 2020 15:17:13 +0200 Subject: [PATCH 01/11] Add LedPwm commands Add commands ``LedPwmOn 0..255``, ``LedPwmOff 0..255`` and ``LedPwmMode1 0/1`` to control led brightness by George (#8491) --- RELEASENOTES.md | 1 + tasmota/CHANGELOG.md | 1 + tasmota/i18n.h | 6 +- tasmota/support_command.ino | 120 ++++++++++++++++++------------------ 4 files changed, 65 insertions(+), 63 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 07406b54f..b5c8332c7 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -58,6 +58,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - 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 support for VEML6075 UVA/UVB/UVINDEX Sensor by device111 (#8432) - Add support for VEML7700 Ambient light intensity Sensor by device111 (#8432) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index b4c77c4ab..b5f6d6ffe 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -5,6 +5,7 @@ - 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 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) ### 8.3.1.1 20200518 diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 98e20d9ac..89d931908 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -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" diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino index 2ee5c17c4..9851a5654 100644 --- a/tasmota/support_command.ino +++ b/tasmota/support_command.ino @@ -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)); - } -} From e69d5686067d9299b831183f8487a56a919a59d5 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sat, 23 May 2020 15:52:59 +0200 Subject: [PATCH 02/11] Use Tasmota optimizations from @s-hadinger in latest Arduino core --- platformio_override_sample.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini index d292724c7..edfb35678 100644 --- a/platformio_override_sample.ini +++ b/platformio_override_sample.ini @@ -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 From 2626345662d5921c3b51cbd3af42f2516bb78590 Mon Sep 17 00:00:00 2001 From: Matthijs Abma <4146168+abmaonline@users.noreply.github.com> Date: Sat, 23 May 2020 17:07:52 +0200 Subject: [PATCH 03/11] Store if current byte is delimiter for later reuse --- tasmota/support_tasmota.ino | 9 ++++++--- tasmota/xdrv_08_serial_bridge.ino | 8 +++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 08a2ac6ca..f3da57ffb 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -1291,10 +1291,13 @@ 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 { diff --git a/tasmota/xdrv_08_serial_bridge.ino b/tasmota/xdrv_08_serial_bridge.ino index a3ae5ae32..66a0cbc3d 100644 --- a/tasmota/xdrv_08_serial_bridge.ino +++ b/tasmota/xdrv_08_serial_bridge.ino @@ -54,11 +54,13 @@ 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 { From 7f18e1e8e3ded01d4d6d765882e3b80aefd837ed Mon Sep 17 00:00:00 2001 From: Matthijs Abma <4146168+abmaonline@users.noreply.github.com> Date: Sat, 23 May 2020 17:30:48 +0200 Subject: [PATCH 04/11] Split logic for adding char to buffer and sending the buffer, to make sure the char doesn't get lost when the buffer is full --- tasmota/support_tasmota.ino | 8 ++++++-- tasmota/xdrv_08_serial_bridge.ino | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index f3da57ffb..671b4ad9d 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -1299,11 +1299,15 @@ void SerialInput(void) if ((serial_in_byte_counter < INPUT_BUFFER_SIZE -1) && // Add char to string if it still fits and ... !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 } } diff --git a/tasmota/xdrv_08_serial_bridge.ino b/tasmota/xdrv_08_serial_bridge.ino index 66a0cbc3d..a713b5cfb 100644 --- a/tasmota/xdrv_08_serial_bridge.ino +++ b/tasmota/xdrv_08_serial_bridge.ino @@ -62,11 +62,15 @@ void SerialBridgeInput(void) if ((serial_bridge_in_byte_counter < SERIAL_BRIDGE_BUFFER_SIZE -1) && // Add char to string if it still fits and ... !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 } } From a5a798853283cd36f46b081fd1f2ec7627e17f0b Mon Sep 17 00:00:00 2001 From: peteakalad Date: Sat, 23 May 2020 21:47:04 +0100 Subject: [PATCH 05/11] Add Adafruit_SGP30_Sensor-1.2.0 --- .../Adafruit_SGP30.cpp | 306 ++++++++++++++++++ .../Adafruit_SGP30.h | 80 +++++ lib/Adafruit_SGP30_Sensor-1.2.0/README.md | 54 ++++ .../examples/sgp30test/sgp30test.ino | 69 ++++ .../library.properties | 10 + lib/Adafruit_SGP30_Sensor-1.2.0/license.txt | 26 ++ lib/Adafruit_SGP30_Sensor-1.2.0/travis.yml | 27 ++ 7 files changed, 572 insertions(+) create mode 100644 lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.cpp create mode 100644 lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.h create mode 100644 lib/Adafruit_SGP30_Sensor-1.2.0/README.md create mode 100644 lib/Adafruit_SGP30_Sensor-1.2.0/examples/sgp30test/sgp30test.ino create mode 100644 lib/Adafruit_SGP30_Sensor-1.2.0/library.properties create mode 100644 lib/Adafruit_SGP30_Sensor-1.2.0/license.txt create mode 100644 lib/Adafruit_SGP30_Sensor-1.2.0/travis.yml diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.cpp b/lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.cpp new file mode 100644 index 000000000..936561ede --- /dev/null +++ b/lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.cpp @@ -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; +} diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.h b/lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.h new file mode 100644 index 000000000..d50099e9d --- /dev/null +++ b/lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.h @@ -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 + +// 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); +}; diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/README.md b/lib/Adafruit_SGP30_Sensor-1.2.0/README.md new file mode 100644 index 000000000..f4d35eeaa --- /dev/null +++ b/lib/Adafruit_SGP30_Sensor-1.2.0/README.md @@ -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) + + + +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 diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/examples/sgp30test/sgp30test.ino b/lib/Adafruit_SGP30_Sensor-1.2.0/examples/sgp30test/sgp30test.ino new file mode 100644 index 000000000..fbfbf49d5 --- /dev/null +++ b/lib/Adafruit_SGP30_Sensor-1.2.0/examples/sgp30test/sgp30test.ino @@ -0,0 +1,69 @@ +#include +#include "Adafruit_SGP30.h" + +Adafruit_SGP30 sgp; + +/* return absolute humidity [mg/m^3] with approximation formula +* @param temperature [°C] +* @param humidity [%RH] +*/ +uint32_t getAbsoluteHumidity(float temperature, float humidity) { + // approximation formula from Sensirion SGP30 Driver Integration chapter 3.15 + const float absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f * exp((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature)); // [g/m^3] + const uint32_t absoluteHumidityScaled = static_cast(1000.0f * absoluteHumidity); // [mg/m^3] + return absoluteHumidityScaled; +} + +void setup() { + Serial.begin(9600); + Serial.println("SGP30 test"); + + if (! sgp.begin()){ + Serial.println("Sensor not found :("); + while (1); + } + Serial.print("Found SGP30 serial #"); + Serial.print(sgp.serialnumber[0], HEX); + Serial.print(sgp.serialnumber[1], HEX); + Serial.println(sgp.serialnumber[2], HEX); + + // If you have a baseline measurement from before you can assign it to start, to 'self-calibrate' + //sgp.setIAQBaseline(0x8E68, 0x8F41); // Will vary for each sensor! +} + +int counter = 0; +void loop() { + // If you have a temperature / humidity sensor, you can set the absolute humidity to enable the humditiy compensation for the air quality signals + //float temperature = 22.1; // [°C] + //float humidity = 45.2; // [%RH] + //sgp.setHumidity(getAbsoluteHumidity(temperature, humidity)); + + if (! sgp.IAQmeasure()) { + Serial.println("Measurement failed"); + return; + } + 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++; + if (counter == 30) { + counter = 0; + + uint16_t TVOC_base, eCO2_base; + if (! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) { + Serial.println("Failed to get baseline readings"); + return; + } + Serial.print("****Baseline values: eCO2: 0x"); Serial.print(eCO2_base, HEX); + Serial.print(" & TVOC: 0x"); Serial.println(TVOC_base, HEX); + } +} diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/library.properties b/lib/Adafruit_SGP30_Sensor-1.2.0/library.properties new file mode 100644 index 000000000..5242ef25b --- /dev/null +++ b/lib/Adafruit_SGP30_Sensor-1.2.0/library.properties @@ -0,0 +1,10 @@ +name=Adafruit SGP30 Sensor +version=1.2.0 +author=Adafruit +maintainer=Adafruit +sentence=This is an Arduino library for the Adafruit SGP30 Gas / Air Quality Sensor +paragraph=This is an Arduino library for the Adafruit SGP30 Gas / Air Quality Sensor +category=Sensors +url=https://github.com/adafruit/Adafruit_SGP30 +architectures=* +depends=Adafruit ILI9341, Adafruit GFX Library diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/license.txt b/lib/Adafruit_SGP30_Sensor-1.2.0/license.txt new file mode 100644 index 000000000..f6a0f22b8 --- /dev/null +++ b/lib/Adafruit_SGP30_Sensor-1.2.0/license.txt @@ -0,0 +1,26 @@ +Software License Agreement (BSD License) + +Copyright (c) 2012, Adafruit Industries +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holders nor the +names of its contributors may be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/travis.yml b/lib/Adafruit_SGP30_Sensor-1.2.0/travis.yml new file mode 100644 index 000000000..428f3434e --- /dev/null +++ b/lib/Adafruit_SGP30_Sensor-1.2.0/travis.yml @@ -0,0 +1,27 @@ +language: c +sudo: false + +# Blacklist +branches: + except: + - gh-pages + +env: + global: + - PRETTYNAME="Adafruit SGP30 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 ILI9341","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) \ No newline at end of file From 615645e50e339a18eef574f92e5ac806091e1401 Mon Sep 17 00:00:00 2001 From: peteakalad Date: Sat, 23 May 2020 22:28:42 +0100 Subject: [PATCH 06/11] Fix for SPG30 Abs Humidity Not Shown on Web UI --- .../.github/ISSUE_TEMPLATE.md | 46 --- .../.github/PULL_REQUEST_TEMPLATE.md | 26 -- lib/Adafruit_SGP30-1.0.3/.gitignore | 4 - lib/Adafruit_SGP30-1.0.3/.travis.yml | 27 -- lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.cpp | 267 ------------------ lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.h | 69 ----- lib/Adafruit_SGP30-1.0.3/README.md | 18 -- .../examples/sgp30test/sgp30test.ino | 61 ---- lib/Adafruit_SGP30-1.0.3/library.properties | 9 - .../Adafruit_SGP30.cpp | 0 .../Adafruit_SGP30.h | 0 .../README.md | 0 .../examples/sgp30test/sgp30test.ino | 0 .../library.properties | 0 .../license.txt | 0 .../travis.yml | 0 lib/Adafruit_SGP30_Sensor-1.2.0/license.txt | 26 -- 17 files changed, 553 deletions(-) delete mode 100755 lib/Adafruit_SGP30-1.0.3/.github/ISSUE_TEMPLATE.md delete mode 100755 lib/Adafruit_SGP30-1.0.3/.github/PULL_REQUEST_TEMPLATE.md delete mode 100755 lib/Adafruit_SGP30-1.0.3/.gitignore delete mode 100755 lib/Adafruit_SGP30-1.0.3/.travis.yml delete mode 100755 lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.cpp delete mode 100755 lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.h delete mode 100755 lib/Adafruit_SGP30-1.0.3/README.md delete mode 100755 lib/Adafruit_SGP30-1.0.3/examples/sgp30test/sgp30test.ino delete mode 100755 lib/Adafruit_SGP30-1.0.3/library.properties rename lib/{Adafruit_SGP30_Sensor-1.2.0 => Adafruit_SGP30-1.2.0}/Adafruit_SGP30.cpp (100%) rename lib/{Adafruit_SGP30_Sensor-1.2.0 => Adafruit_SGP30-1.2.0}/Adafruit_SGP30.h (100%) rename lib/{Adafruit_SGP30_Sensor-1.2.0 => Adafruit_SGP30-1.2.0}/README.md (100%) rename lib/{Adafruit_SGP30_Sensor-1.2.0 => Adafruit_SGP30-1.2.0}/examples/sgp30test/sgp30test.ino (100%) rename lib/{Adafruit_SGP30_Sensor-1.2.0 => Adafruit_SGP30-1.2.0}/library.properties (100%) rename lib/{Adafruit_SGP30-1.0.3 => Adafruit_SGP30-1.2.0}/license.txt (100%) mode change 100755 => 100644 rename lib/{Adafruit_SGP30_Sensor-1.2.0 => Adafruit_SGP30-1.2.0}/travis.yml (100%) delete mode 100644 lib/Adafruit_SGP30_Sensor-1.2.0/license.txt diff --git a/lib/Adafruit_SGP30-1.0.3/.github/ISSUE_TEMPLATE.md b/lib/Adafruit_SGP30-1.0.3/.github/ISSUE_TEMPLATE.md deleted file mode 100755 index f0e26146f..000000000 --- a/lib/Adafruit_SGP30-1.0.3/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -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** diff --git a/lib/Adafruit_SGP30-1.0.3/.github/PULL_REQUEST_TEMPLATE.md b/lib/Adafruit_SGP30-1.0.3/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100755 index 7b641eb86..000000000 --- a/lib/Adafruit_SGP30-1.0.3/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -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. diff --git a/lib/Adafruit_SGP30-1.0.3/.gitignore b/lib/Adafruit_SGP30-1.0.3/.gitignore deleted file mode 100755 index 7f189125f..000000000 --- a/lib/Adafruit_SGP30-1.0.3/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*~ -Doxyfile* -doxygen_sqlite3.db -html \ No newline at end of file diff --git a/lib/Adafruit_SGP30-1.0.3/.travis.yml b/lib/Adafruit_SGP30-1.0.3/.travis.yml deleted file mode 100755 index 428f3434e..000000000 --- a/lib/Adafruit_SGP30-1.0.3/.travis.yml +++ /dev/null @@ -1,27 +0,0 @@ -language: c -sudo: false - -# Blacklist -branches: - except: - - gh-pages - -env: - global: - - PRETTYNAME="Adafruit SGP30 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 ILI9341","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) \ No newline at end of file diff --git a/lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.cpp b/lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.cpp deleted file mode 100755 index ce6116863..000000000 --- a/lib/Adafruit_SGP30-1.0.3/Adafruit_SGP30.cpp +++ /dev/null @@ -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; iwrite(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; iread(); -#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 - -// 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); -}; diff --git a/lib/Adafruit_SGP30-1.0.3/README.md b/lib/Adafruit_SGP30-1.0.3/README.md deleted file mode 100755 index 44056b5c8..000000000 --- a/lib/Adafruit_SGP30-1.0.3/README.md +++ /dev/null @@ -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 - diff --git a/lib/Adafruit_SGP30-1.0.3/examples/sgp30test/sgp30test.ino b/lib/Adafruit_SGP30-1.0.3/examples/sgp30test/sgp30test.ino deleted file mode 100755 index b7ff8a70c..000000000 --- a/lib/Adafruit_SGP30-1.0.3/examples/sgp30test/sgp30test.ino +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include "Adafruit_SGP30.h" - -Adafruit_SGP30 sgp; - -/* return absolute humidity [mg/m^3] with approximation formula -* @param temperature [°C] -* @param humidity [%RH] -*/ -uint32_t getAbsoluteHumidity(float temperature, float humidity) { - // approximation formula from Sensirion SGP30 Driver Integration chapter 3.15 - const float absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f * exp((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature)); // [g/m^3] - const uint32_t absoluteHumidityScaled = static_cast(1000.0f * absoluteHumidity); // [mg/m^3] - return absoluteHumidityScaled; -} - -void setup() { - Serial.begin(9600); - Serial.println("SGP30 test"); - - if (! sgp.begin()){ - Serial.println("Sensor not found :("); - while (1); - } - Serial.print("Found SGP30 serial #"); - Serial.print(sgp.serialnumber[0], HEX); - Serial.print(sgp.serialnumber[1], HEX); - Serial.println(sgp.serialnumber[2], HEX); - - // If you have a baseline measurement from before you can assign it to start, to 'self-calibrate' - //sgp.setIAQBaseline(0x8E68, 0x8F41); // Will vary for each sensor! -} - -int counter = 0; -void loop() { - // If you have a temperature / humidity sensor, you can set the absolute humidity to enable the humditiy compensation for the air quality signals - //float temperature = 22.1; // [°C] - //float humidity = 45.2; // [%RH] - //sgp.setHumidity(getAbsoluteHumidity(temperature, humidity)); - - if (! sgp.IAQmeasure()) { - Serial.println("Measurement failed"); - return; - } - Serial.print("TVOC "); Serial.print(sgp.TVOC); Serial.print(" ppb\t"); - Serial.print("eCO2 "); Serial.print(sgp.eCO2); Serial.println(" ppm"); - delay(1000); - - counter++; - if (counter == 30) { - counter = 0; - - uint16_t TVOC_base, eCO2_base; - if (! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) { - Serial.println("Failed to get baseline readings"); - return; - } - Serial.print("****Baseline values: eCO2: 0x"); Serial.print(eCO2_base, HEX); - Serial.print(" & TVOC: 0x"); Serial.println(TVOC_base, HEX); - } -} \ No newline at end of file diff --git a/lib/Adafruit_SGP30-1.0.3/library.properties b/lib/Adafruit_SGP30-1.0.3/library.properties deleted file mode 100755 index 6c86464d1..000000000 --- a/lib/Adafruit_SGP30-1.0.3/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=Adafruit SGP30 Sensor -version=1.0.3 -author=Adafruit -maintainer=Adafruit -sentence=This is an Arduino library for the Adafruit SGP30 Gas / Air Quality Sensor -paragraph=This is an Arduino library for the Adafruit SGP30 Gas / Air Quality Sensor -category=Sensors -url=https://github.com/adafruit/Adafruit_SGP30 -architectures=* diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.cpp b/lib/Adafruit_SGP30-1.2.0/Adafruit_SGP30.cpp similarity index 100% rename from lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.cpp rename to lib/Adafruit_SGP30-1.2.0/Adafruit_SGP30.cpp diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.h b/lib/Adafruit_SGP30-1.2.0/Adafruit_SGP30.h similarity index 100% rename from lib/Adafruit_SGP30_Sensor-1.2.0/Adafruit_SGP30.h rename to lib/Adafruit_SGP30-1.2.0/Adafruit_SGP30.h diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/README.md b/lib/Adafruit_SGP30-1.2.0/README.md similarity index 100% rename from lib/Adafruit_SGP30_Sensor-1.2.0/README.md rename to lib/Adafruit_SGP30-1.2.0/README.md diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/examples/sgp30test/sgp30test.ino b/lib/Adafruit_SGP30-1.2.0/examples/sgp30test/sgp30test.ino similarity index 100% rename from lib/Adafruit_SGP30_Sensor-1.2.0/examples/sgp30test/sgp30test.ino rename to lib/Adafruit_SGP30-1.2.0/examples/sgp30test/sgp30test.ino diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/library.properties b/lib/Adafruit_SGP30-1.2.0/library.properties similarity index 100% rename from lib/Adafruit_SGP30_Sensor-1.2.0/library.properties rename to lib/Adafruit_SGP30-1.2.0/library.properties diff --git a/lib/Adafruit_SGP30-1.0.3/license.txt b/lib/Adafruit_SGP30-1.2.0/license.txt old mode 100755 new mode 100644 similarity index 100% rename from lib/Adafruit_SGP30-1.0.3/license.txt rename to lib/Adafruit_SGP30-1.2.0/license.txt diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/travis.yml b/lib/Adafruit_SGP30-1.2.0/travis.yml similarity index 100% rename from lib/Adafruit_SGP30_Sensor-1.2.0/travis.yml rename to lib/Adafruit_SGP30-1.2.0/travis.yml diff --git a/lib/Adafruit_SGP30_Sensor-1.2.0/license.txt b/lib/Adafruit_SGP30_Sensor-1.2.0/license.txt deleted file mode 100644 index f6a0f22b8..000000000 --- a/lib/Adafruit_SGP30_Sensor-1.2.0/license.txt +++ /dev/null @@ -1,26 +0,0 @@ -Software License Agreement (BSD License) - -Copyright (c) 2012, Adafruit Industries -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. -3. Neither the name of the copyright holders nor the -names of its contributors may be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 11cb3e838c88c5e3c51813667353d3c9e3b26e59 Mon Sep 17 00:00:00 2001 From: peteakalad Date: Sat, 23 May 2020 22:41:50 +0100 Subject: [PATCH 07/11] Fix for SGP30 for Abs Humidity on Web UI --- tasmota/xsns_21_sgp30.ino | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tasmota/xsns_21_sgp30.ino b/tasmota/xsns_21_sgp30.ino index e3aa2e9e8..ab417a8c3 100644 --- a/tasmota/xsns_21_sgp30.ino +++ b/tasmota/xsns_21_sgp30.ino @@ -118,12 +118,14 @@ 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(); From f5f1bdefdbe42b3e20b36f217594dc7f2c1ee306 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sun, 24 May 2020 09:57:11 +0200 Subject: [PATCH 08/11] Add wildcard patter for JSON marching in rules using ``?`` pattern --- tasmota/CHANGELOG.md | 1 + tasmota/support.ino | 59 --------------- tasmota/support_json.ino | 106 +++++++++++++++++++++++++++ tasmota/xdrv_10_rules.ino | 29 ++++---- tasmota/xdrv_20_hue.ino | 4 +- tasmota/xdrv_23_zigbee_1_headers.ino | 22 +----- tasmota/xdrv_23_zigbee_2_devices.ino | 8 +- tasmota/xdrv_23_zigbee_3_hue.ino | 6 +- tasmota/xdrv_23_zigbee_8_parsers.ino | 2 +- tasmota/xdrv_23_zigbee_9_impl.ino | 44 +++++------ 10 files changed, 154 insertions(+), 127 deletions(-) create mode 100644 tasmota/support_json.ino diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index b5f6d6ffe..02ae08cb9 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -6,6 +6,7 @@ - Change Energy JSON ExportActive field from ``"ExportActive":[33.736,11.717,16.978]`` to ``"ExportActive":33.736,"ExportTariff":[11.717,16.978]`` - 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 patter for JSON marching in rules using ``?`` pattern ### 8.3.1.1 20200518 diff --git a/tasmota/support.ino b/tasmota/support.ino index cecb5ffb2..c5006cf6c 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -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 \*********************************************************************************************/ diff --git a/tasmota/support_json.ino b/tasmota/support_json.ino new file mode 100644 index 000000000..eb933fc2d --- /dev/null +++ b/tasmota/support_json.ino @@ -0,0 +1,106 @@ +/* + support_json.ino - Static binary buffer for Zigbee on Tasmota + + 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 . +*/ + +/*********************************************************************************************\ + * 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; +} diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino index 912a32ae5..1ad618485 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -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()); + 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()); } } diff --git a/tasmota/xdrv_20_hue.ino b/tasmota/xdrv_20_hue.ino index 639f198bb..17a0886db 100644 --- a/tasmota/xdrv_20_hue.ino +++ b/tasmota/xdrv_20_hue.ino @@ -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; diff --git a/tasmota/xdrv_23_zigbee_1_headers.ino b/tasmota/xdrv_23_zigbee_1_headers.ino index 5a250c781..046450927 100644 --- a/tasmota/xdrv_23_zigbee_1_headers.ino +++ b/tasmota/xdrv_23_zigbee_1_headers.ino @@ -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(); if (strlen(val_cs)) { diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino index 74d52c73e..c115052f7 100644 --- a/tasmota/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/xdrv_23_zigbee_2_devices.ino @@ -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(), 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())) { const JsonArray &arr_ep = val_endpoints.as(); endpoints_len = arr_ep.size(); diff --git a/tasmota/xdrv_23_zigbee_3_hue.ino b/tasmota/xdrv_23_zigbee_3_hue.ino index d5c4b1f08..e4e8a8b63 100644 --- a/tasmota/xdrv_23_zigbee_3_hue.ino +++ b/tasmota/xdrv_23_zigbee_3_hue.ino @@ -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; diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index 20aecd0f9..ffe108c5a 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -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); diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index 40053c5a7..5c5181b6d 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -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()); 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()); } @@ -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()); 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()); 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()) { @@ -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(), 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(), 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(), nullptr, 0); } // Check if a parameter was changed after all From fe045c3c9d8c6b3ffce57cd6c63bed7d3deb589a Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sun, 24 May 2020 10:00:32 +0200 Subject: [PATCH 09/11] Fixed description of file --- tasmota/support_json.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/support_json.ino b/tasmota/support_json.ino index eb933fc2d..51fa0e6a3 100644 --- a/tasmota/support_json.ino +++ b/tasmota/support_json.ino @@ -1,5 +1,5 @@ /* - support_json.ino - Static binary buffer for Zigbee on Tasmota + support_json.ino - JSON support functions Copyright (C) 2020 Theo Arends and Stephan Hadinger From 6073bd9eb664e986f0befbfc83aa578ad34427ef Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 24 May 2020 11:55:39 +0200 Subject: [PATCH 10/11] Update changelog --- RELEASENOTES.md | 2 ++ tasmota/CHANGELOG.md | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b5c8332c7..f376168d1 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -55,11 +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 diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 02ae08cb9..472cb9ffa 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -4,9 +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 patter for JSON marching in rules using ``?`` pattern +- Add wildcard pattern ``?`` for JSON matching in rules ### 8.3.1.1 20200518 From ebbbd37865b9e83bd3145f00b89ee6053f73f863 Mon Sep 17 00:00:00 2001 From: device111 <48546979+device111@users.noreply.github.com> Date: Sun, 24 May 2020 12:28:08 +0200 Subject: [PATCH 11/11] add private pow function in lib for future use alternate pow function for future use in Tasmota to calculate normalized Lux and normalized white values. --- lib/Adafruit_VEML7700/Adafruit_VEML7700.cpp | 27 +++++++++++++++++++-- lib/Adafruit_VEML7700/Adafruit_VEML7700.h | 6 +++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/lib/Adafruit_VEML7700/Adafruit_VEML7700.cpp b/lib/Adafruit_VEML7700/Adafruit_VEML7700.cpp index 001d4995e..b719af9fa 100644 --- a/lib/Adafruit_VEML7700/Adafruit_VEML7700.cpp +++ b/lib/Adafruit_VEML7700/Adafruit_VEML7700.cpp @@ -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; diff --git a/lib/Adafruit_VEML7700/Adafruit_VEML7700.h b/lib/Adafruit_VEML7700/Adafruit_VEML7700.h index b842a7bc1..13a0bded5 100644 --- a/lib/Adafruit_VEML7700/Adafruit_VEML7700.h +++ b/lib/Adafruit_VEML7700/Adafruit_VEML7700.h @@ -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;