From 66679515053d907c7cd81234121a0bcb106df197 Mon Sep 17 00:00:00 2001
From: Robert Jaakke
Date: Sat, 6 Jun 2020 22:35:41 +0200
Subject: [PATCH 01/35] Working sample
---
lib/LOLIN_HP303B/README.md | 4 +
.../i2c_background/i2c_background.ino | 98 +
.../examples/i2c_command/i2c_command.ino | 75 +
.../examples/i2c_interrupt/i2c_interrupt.ino | 112 ++
lib/LOLIN_HP303B/keywords.txt | 26 +
lib/LOLIN_HP303B/library.properties | 9 +
lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp | 1668 +++++++++++++++++
lib/LOLIN_HP303B/src/LOLIN_HP303B.h | 144 ++
lib/LOLIN_HP303B/src/util/hp303b_consts.h | 258 +++
tasmota/support_features.ino | 4 +-
tasmota/xsns_73_hp303b.ino | 161 ++
tools/decode-status.py | 2 +-
12 files changed, 2559 insertions(+), 2 deletions(-)
create mode 100644 lib/LOLIN_HP303B/README.md
create mode 100644 lib/LOLIN_HP303B/examples/i2c_background/i2c_background.ino
create mode 100644 lib/LOLIN_HP303B/examples/i2c_command/i2c_command.ino
create mode 100644 lib/LOLIN_HP303B/examples/i2c_interrupt/i2c_interrupt.ino
create mode 100644 lib/LOLIN_HP303B/keywords.txt
create mode 100644 lib/LOLIN_HP303B/library.properties
create mode 100644 lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp
create mode 100644 lib/LOLIN_HP303B/src/LOLIN_HP303B.h
create mode 100644 lib/LOLIN_HP303B/src/util/hp303b_consts.h
create mode 100644 tasmota/xsns_73_hp303b.ino
diff --git a/lib/LOLIN_HP303B/README.md b/lib/LOLIN_HP303B/README.md
new file mode 100644
index 000000000..24d6c2d1b
--- /dev/null
+++ b/lib/LOLIN_HP303B/README.md
@@ -0,0 +1,4 @@
+# Arduino library for the HP303B
+### Installation
+- Clone this repository or download&unzip [zip file](https://github.com/wemos/LOLIN_HP303B_Library/archive/master.zip) into Arduino/libraries
+
diff --git a/lib/LOLIN_HP303B/examples/i2c_background/i2c_background.ino b/lib/LOLIN_HP303B/examples/i2c_background/i2c_background.ino
new file mode 100644
index 000000000..1b9ae6e68
--- /dev/null
+++ b/lib/LOLIN_HP303B/examples/i2c_background/i2c_background.ino
@@ -0,0 +1,98 @@
+#include
+
+// HP303B Opject
+LOLIN_HP303B HP303BPressureSensor = LOLIN_HP303B();
+
+void setup()
+{
+ Serial.begin(115200);
+ while (!Serial);
+
+ //Call begin to initialize HP303BPressureSensor
+ //The parameter 0x76 is the bus address. The default address is 0x77 and does not need to be given.
+ //HP303BPressureSensor.begin(Wire, 0x76);
+ //Use the commented line below instead to use the default I2C address.
+ HP303BPressureSensor.begin();
+
+ //temperature measure rate (value from 0 to 7)
+ //2^temp_mr temperature measurement results per second
+ int16_t temp_mr = 2;
+ //temperature oversampling rate (value from 0 to 7)
+ //2^temp_osr internal temperature measurements per result
+ //A higher value increases precision
+ int16_t temp_osr = 2;
+ //pressure measure rate (value from 0 to 7)
+ //2^prs_mr pressure measurement results per second
+ int16_t prs_mr = 2;
+ //pressure oversampling rate (value from 0 to 7)
+ //2^prs_osr internal pressure measurements per result
+ //A higher value increases precision
+ int16_t prs_osr = 2;
+ //startMeasureBothCont enables background mode
+ //temperature and pressure ar measured automatically
+ //High precision and hgh measure rates at the same time are not available.
+ //Consult Datasheet (or trial and error) for more information
+ int16_t ret = HP303BPressureSensor.startMeasureBothCont(temp_mr, temp_osr, prs_mr, prs_osr);
+ //Use one of the commented lines below instead to measure only temperature or pressure
+ //int16_t ret = HP303BPressureSensor.startMeasureTempCont(temp_mr, temp_osr);
+ //int16_t ret = HP303BPressureSensor.startMeasurePressureCont(prs_mr, prs_osr);
+
+
+ if (ret != 0)
+ {
+ Serial.print("Init FAILED! ret = ");
+ Serial.println(ret);
+ }
+ else
+ {
+ Serial.println("Init complete!");
+ }
+}
+
+
+
+void loop()
+{
+ unsigned char pressureCount = 20;
+ int32_t pressure[pressureCount];
+ unsigned char temperatureCount = 20;
+ int32_t temperature[temperatureCount];
+
+ //This function writes the results of continuous measurements to the arrays given as parameters
+ //The parameters temperatureCount and pressureCount should hold the sizes of the arrays temperature and pressure when the function is called
+ //After the end of the function, temperatureCount and pressureCount hold the numbers of values written to the arrays
+ //Note: The HP303B cannot save more than 32 results. When its result buffer is full, it won't save any new measurement results
+ int16_t ret = HP303BPressureSensor.getContResults(temperature, temperatureCount, pressure, pressureCount);
+
+ if (ret != 0)
+ {
+ Serial.println();
+ Serial.println();
+ Serial.print("FAIL! ret = ");
+ Serial.println(ret);
+ }
+ else
+ {
+ Serial.println();
+ Serial.println();
+ Serial.print(temperatureCount);
+ Serial.println(" temperature values found: ");
+ for (int16_t i = 0; i < temperatureCount; i++)
+ {
+ Serial.print(temperature[i]);
+ Serial.println(" degrees of Celsius");
+ }
+
+ Serial.println();
+ Serial.print(pressureCount);
+ Serial.println(" pressure values found: ");
+ for (int16_t i = 0; i < pressureCount; i++)
+ {
+ Serial.print(pressure[i]);
+ Serial.println(" Pascal");
+ }
+ }
+
+ //Wait some time, so that the HP303B can refill its buffer
+ delay(10000);
+}
diff --git a/lib/LOLIN_HP303B/examples/i2c_command/i2c_command.ino b/lib/LOLIN_HP303B/examples/i2c_command/i2c_command.ino
new file mode 100644
index 000000000..e629f7d5f
--- /dev/null
+++ b/lib/LOLIN_HP303B/examples/i2c_command/i2c_command.ino
@@ -0,0 +1,75 @@
+#include
+
+// HP303B Opject
+LOLIN_HP303B HP303BPressureSensor;
+
+
+void setup()
+{
+ Serial.begin(115200);
+ while (!Serial);
+
+
+ //Call begin to initialize HP303BPressureSensor
+ //The parameter 0x76 is the bus address. The default address is 0x77 and does not need to be given.
+ //HP303BPressureSensor.begin(Wire, 0x76);
+ //Use the commented line below instead of the one above to use the default I2C address.
+ //if you are using the Pressure 3 click Board, you need 0x76
+ HP303BPressureSensor.begin();
+
+ Serial.println("Init complete!");
+}
+
+
+
+void loop()
+{
+ int32_t temperature;
+ int32_t pressure;
+ int16_t oversampling = 7;
+ int16_t ret;
+ Serial.println();
+
+ //lets the HP303B perform a Single temperature measurement with the last (or standard) configuration
+ //The result will be written to the paramerter temperature
+ //ret = HP303BPressureSensor.measureTempOnce(temperature);
+ //the commented line below does exactly the same as the one above, but you can also config the precision
+ //oversampling can be a value from 0 to 7
+ //the HP303B will perform 2^oversampling internal temperature measurements and combine them to one result with higher precision
+ //measurements with higher precision take more time, consult datasheet for more information
+ ret = HP303BPressureSensor.measureTempOnce(temperature, oversampling);
+
+ if (ret != 0)
+ {
+ //Something went wrong.
+ //Look at the library code for more information about return codes
+ Serial.print("FAIL! ret = ");
+ Serial.println(ret);
+ }
+ else
+ {
+ Serial.print("Temperature: ");
+ Serial.print(temperature);
+ Serial.println(" degrees of Celsius");
+ }
+
+ //Pressure measurement behaves like temperature measurement
+ //ret = HP303BPressureSensor.measurePressureOnce(pressure);
+ ret = HP303BPressureSensor.measurePressureOnce(pressure, oversampling);
+ if (ret != 0)
+ {
+ //Something went wrong.
+ //Look at the library code for more information about return codes
+ Serial.print("FAIL! ret = ");
+ Serial.println(ret);
+ }
+ else
+ {
+ Serial.print("Pressure: ");
+ Serial.print(pressure);
+ Serial.println(" Pascal");
+ }
+
+ //Wait some time
+ delay(500);
+}
diff --git a/lib/LOLIN_HP303B/examples/i2c_interrupt/i2c_interrupt.ino b/lib/LOLIN_HP303B/examples/i2c_interrupt/i2c_interrupt.ino
new file mode 100644
index 000000000..d1221109b
--- /dev/null
+++ b/lib/LOLIN_HP303B/examples/i2c_interrupt/i2c_interrupt.ino
@@ -0,0 +1,112 @@
+#include
+
+// HP303B Opject
+LOLIN_HP303B HP303BPressureSensor = LOLIN_HP303B();
+
+void onFifoFull();
+
+const unsigned char pressureLength = 50;
+unsigned char pressureCount = 0;
+int32_t pressure[pressureLength];
+unsigned char temperatureCount = 0;
+const unsigned char temperatureLength = 50;
+int32_t temperature[temperatureLength];
+
+
+
+void setup()
+{
+ Serial.begin(115200);
+ while (!Serial);
+
+ //Call begin to initialize HP303BPressureSensor
+ //The parameter 0x76 is the bus address. The default address is 0x77 and does not need to be given.
+ //HP303BPressureSensor.begin(Wire, 0x76);
+ //Use the commented line below instead to use the default I2C address.
+ HP303BPressureSensor.begin();
+
+ int16_t ret = HP303BPressureSensor.setInterruptPolarity(1);
+ ret = HP303BPressureSensor.setInterruptSources(1, 0, 0);
+ //clear interrupt flag by reading
+ HP303BPressureSensor.getIntStatusFifoFull();
+
+ //initialization of Interrupt for Controller unit
+ //SDO pin of HP303B has to be connected with interrupt pin
+ int16_t interruptPin = 3;
+ pinMode(interruptPin, INPUT);
+ attachInterrupt(digitalPinToInterrupt(interruptPin), onFifoFull, RISING);
+
+ //start of a continuous measurement just like before
+ int16_t temp_mr = 3;
+ int16_t temp_osr = 2;
+ int16_t prs_mr = 1;
+ int16_t prs_osr = 3;
+ ret = HP303BPressureSensor.startMeasureBothCont(temp_mr, temp_osr, prs_mr, prs_osr);
+ if (ret != 0)
+ {
+ Serial.print("Init FAILED! ret = ");
+ Serial.println(ret);
+ }
+ else
+ {
+ Serial.println("Init complete!");
+ }
+}
+
+
+void loop()
+{
+ //do other stuff
+ Serial.println("loop running");
+ delay(500);
+
+
+ //if result arrays are full
+ //This could also be in the interrupt handler, but it would take too much time for a proper ISR
+ if (pressureCount == pressureLength && temperatureCount == temperatureLength)
+ {
+ //print results
+ Serial.println();
+ Serial.println();
+ Serial.print(temperatureCount);
+ Serial.println(" temperature values found: ");
+ for (int16_t i = 0; i < temperatureCount; i++)
+ {
+ Serial.print(temperature[i]);
+ Serial.println(" degrees of Celsius");
+ }
+ Serial.println();
+ Serial.print(pressureCount);
+ Serial.println(" pressure values found: ");
+ for (int16_t i = 0; i < pressureCount; i++)
+ {
+ Serial.print(pressure[i]);
+ Serial.println(" Pascal");
+ }
+ Serial.println();
+ Serial.println();
+ //reset result counters
+ pressureCount = 0;
+ temperatureCount = 0;
+ }
+}
+
+
+//interrupt handler
+void onFifoFull()
+{
+ //message for debugging
+ Serial.println("Interrupt handler called");
+
+ //clear interrupt flag by reading
+ HP303BPressureSensor.getIntStatusFifoFull();
+
+ //calculate the number of free indexes in the result arrays
+ unsigned char prs_freespace = pressureLength - pressureCount;
+ unsigned char temp_freespace = temperatureLength - temperatureCount;
+ //read the results from HP303B, new results will be added at the end of the arrays
+ HP303BPressureSensor.getContResults(&temperature[temperatureCount], temp_freespace, &pressure[pressureCount], prs_freespace);
+ //after reading the result counters are increased by the amount of new results
+ pressureCount += prs_freespace;
+ temperatureCount += temp_freespace;
+}
diff --git a/lib/LOLIN_HP303B/keywords.txt b/lib/LOLIN_HP303B/keywords.txt
new file mode 100644
index 000000000..b283317f5
--- /dev/null
+++ b/lib/LOLIN_HP303B/keywords.txt
@@ -0,0 +1,26 @@
+#######################################
+# Syntax Coloring Map For DHT12
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+DHT12 KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+get KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+
+cTemp LITERAL1
+fTemp LITERAL1
+humidity LITERAL1
+
+
+
diff --git a/lib/LOLIN_HP303B/library.properties b/lib/LOLIN_HP303B/library.properties
new file mode 100644
index 000000000..7f71ad2b2
--- /dev/null
+++ b/lib/LOLIN_HP303B/library.properties
@@ -0,0 +1,9 @@
+name=LOLIN_HP303B
+version=1.0.0
+author=WEMOS.CC
+maintainer=WEMOS.CC
+sentence=Library for the HP303B..
+paragraph=LOLIN HP303B
+category=Device Control
+url=https://github.com/wemos/LOLIN_HP303B_Library
+architectures=*
diff --git a/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp b/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp
new file mode 100644
index 000000000..43fcd435e
--- /dev/null
+++ b/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp
@@ -0,0 +1,1668 @@
+#include "LOLIN_HP303B.h"
+
+
+
+const int32_t LOLIN_HP303B::scaling_facts[HP303B__NUM_OF_SCAL_FACTS]
+ = {524288, 1572864, 3670016, 7864320, 253952, 516096, 1040384, 2088960};
+
+
+
+//////// Constructor, Destructor, begin, end ////////
+
+
+/**
+ * Standard Constructor
+ */
+LOLIN_HP303B::LOLIN_HP303B(void)
+{
+ //assume that initialization has failed before it has been done
+ m_initFail = 1U;
+}
+
+/**
+ * Standard Destructor
+ */
+LOLIN_HP303B::~LOLIN_HP303B(void)
+{
+ end();
+}
+
+
+
+/**
+ * Standard I2C begin function
+ *
+ * &bus: I2CBus which connects MC to HP303B
+ * slaveAddress: Address of the HP303B (0x77 or 0x76)
+ */
+void LOLIN_HP303B::begin(TwoWire &bus, uint8_t slaveAddress)
+{
+ //this flag will show if the initialization was successful
+ m_initFail = 0U;
+
+ //Set I2C bus connection
+ m_SpiI2c = 1U;
+ m_i2cbus = &bus;
+ m_slaveAddress = slaveAddress;
+
+ // Init bus
+ m_i2cbus->begin();
+
+ delay(50); //startup time of HP303B
+
+ init();
+}
+
+void LOLIN_HP303B::begin(uint8_t slaveAddress)
+{
+ begin(Wire,slaveAddress);
+}
+
+/**
+ * SPI begin function for HP303B with 4-wire SPI
+ */
+void LOLIN_HP303B::begin(SPIClass &bus, int32_t chipSelect)
+{
+ begin(bus, chipSelect, 0U);
+}
+
+/**
+ * Standard SPI begin function
+ *
+ * &bus: SPI bus which connects MC to HP303B
+ * chipSelect: Number of the CS line for the HP303B
+ * threeWire: 1 if HP303B is connected with 3-wire SPI
+ * 0 if HP303B is connected with 4-wire SPI (standard)
+ */
+void LOLIN_HP303B::begin(SPIClass &bus, int32_t chipSelect, uint8_t threeWire)
+{
+ //this flag will show if the initialization was successful
+ m_initFail = 0U;
+
+ //Set SPI bus connection
+ m_SpiI2c = 0U;
+ m_spibus = &bus;
+ m_chipSelect = chipSelect;
+
+ // Init bus
+ m_spibus->begin();
+ m_spibus->setDataMode(SPI_MODE3);
+
+ pinMode(m_chipSelect, OUTPUT);
+ digitalWrite(m_chipSelect, HIGH);
+
+ delay(50); //startup time of HP303B
+
+ //switch to 3-wire mode if necessary
+ //do not use writeByteBitfield or check option to set SPI mode!
+ //Reading is not possible until SPI-mode is valid
+ if(threeWire)
+ {
+ m_threeWire = 1U;
+ if(writeByte(HP303B__REG_ADR_SPI3W, HP303B__REG_CONTENT_SPI3W))
+ {
+ m_initFail = 1U;
+ return;
+ }
+ }
+
+ init();
+}
+
+/**
+ * End function for HP303B
+ * Sets the sensor to idle mode
+ */
+void LOLIN_HP303B::end(void)
+{
+ standby();
+}
+
+
+//////// Declaration of other public functions starts here ////////
+
+
+/**
+ * returns the Product ID of the connected HP303B sensor
+ */
+uint8_t LOLIN_HP303B::getProductId(void)
+{
+ return m_productID;
+}
+
+/**
+ * returns the Revision ID of the connected HP303B sensor
+ */
+uint8_t LOLIN_HP303B::getRevisionId(void)
+{
+ return m_revisionID;
+}
+
+/**
+ * Sets the HP303B to standby mode
+ *
+ * returns: 0 on success
+ * -2 if object initialization failed
+ * -1 on other fail
+ */
+int16_t LOLIN_HP303B::standby(void)
+{
+ //abort if initialization failed
+ if(m_initFail)
+ {
+ return HP303B__FAIL_INIT_FAILED;
+ }
+ //set device to idling mode
+ int16_t ret = setOpMode(IDLE);
+ if(ret != HP303B__SUCCEEDED)
+ {
+ return ret;
+ }
+ //flush the FIFO
+ ret = writeByteBitfield(1U, HP303B__REG_INFO_FIFO_FL);
+ if(ret < 0)
+ {
+ return ret;
+ }
+ //disable the FIFO
+ ret = writeByteBitfield(0U, HP303B__REG_INFO_FIFO_EN);
+ return ret;
+}
+
+
+/**
+ * performs one temperature measurement and writes result to the given address
+ *
+ * &result: reference to a 32-Bit signed Integer value where the result will be written
+ * It will not be written if result==NULL
+ * returns: 0 on success
+ * -4 if the HP303B is could not finish its measurement in time
+ * -3 if the HP303B is already busy
+ * -2 if the object initialization failed
+ * -1 on other fail
+ */
+int16_t LOLIN_HP303B::measureTempOnce(int32_t &result)
+{
+ return measureTempOnce(result, m_tempOsr);
+}
+
+/**
+ * performs one temperature measurement and writes result to the given address
+ * the desired precision can be set with oversamplingRate
+ *
+ * &result: reference to a 32-Bit signed Integer where the result will be written
+ * It will not be written if result==NULL
+ * oversamplingRate: a value from 0 to 7 that decides about the precision
+ * of the measurement
+ * If this value equals n, the HP303B will perform
+ * 2^n measurements and combine the results
+ * returns: 0 on success
+ * -4 if the HP303B is could not finish its measurement in time
+ * -3 if the HP303B is already busy
+ * -2 if the object initialization failed
+ * -1 on other fail
+ */
+int16_t LOLIN_HP303B::measureTempOnce(int32_t &result, uint8_t oversamplingRate)
+{
+ //Start measurement
+ int16_t ret = startMeasureTempOnce(oversamplingRate);
+ if(ret!=HP303B__SUCCEEDED)
+ {
+ return ret;
+ }
+
+ //wait until measurement is finished
+ delay(calcBusyTime(0U, m_tempOsr)/HP303B__BUSYTIME_SCALING);
+ delay(HP303B__BUSYTIME_FAILSAFE);
+
+ ret = getSingleResult(result);
+ if(ret!=HP303B__SUCCEEDED)
+ {
+ standby();
+ }
+ return ret;
+}
+
+/**
+ * starts a single temperature measurement
+ *
+ * returns: 0 on success
+ * -3 if the HP303B is already busy
+ * -2 if the object initialization failed
+ * -1 on other fail
+ */
+int16_t LOLIN_HP303B::startMeasureTempOnce(void)
+{
+ return startMeasureTempOnce(m_tempOsr);
+}
+
+/**
+ * starts a single temperature measurement
+ * The desired precision can be set with oversamplingRate
+ *
+ * oversamplingRate: a value from 0 to 7 that decides about the precision
+ * of the measurement
+ * If this value equals n, the HP303B will perform
+ * 2^n measurements and combine the results
+ * returns: 0 on success
+ * -3 if the HP303B is already busy
+ * -2 if the object initialization failed
+ * -1 on other fail
+ */
+int16_t LOLIN_HP303B::startMeasureTempOnce(uint8_t oversamplingRate)
+{
+ //abort if initialization failed
+ if(m_initFail)
+ {
+ return HP303B__FAIL_INIT_FAILED;
+ }
+ //abort if device is not in idling mode
+ if(m_opMode!=IDLE)
+ {
+ return HP303B__FAIL_TOOBUSY;
+ }
+
+ if(oversamplingRate!=m_tempOsr)
+ {
+ //configuration of oversampling rate
+ if(configTemp(0U, oversamplingRate) != HP303B__SUCCEEDED)
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ }
+
+ //set device to temperature measuring mode
+ return setOpMode(0U, 1U, 0U);
+}
+
+/**
+ * performs one pressure measurement and writes result to the given address
+ *
+ * &result: reference to a 32-Bit signed Integer value where the result will be written
+ * It will not be written if result==NULL
+ * returns: 0 on success
+ * -4 if the HP303B is could not finish its measurement in time
+ * -3 if the HP303B is already busy
+ * -2 if the object initialization failed
+ * -1 on other fail
+ */
+int16_t LOLIN_HP303B::measurePressureOnce(int32_t &result)
+{
+ return measurePressureOnce(result, m_prsOsr);
+}
+
+/**
+ * performs one pressure measurement and writes result to the given address
+ * the desired precision can be set with oversamplingRate
+ *
+ * &result: reference to a 32-Bit signed Integer where the result will be written
+ * It will not be written if result==NULL
+ * oversamplingRate: a value from 0 to 7 that decides about the precision
+ * of the measurement
+ * If this value equals n, the HP303B will perform
+ * 2^n measurements and combine the results
+ * returns: 0 on success
+ * -4 if the HP303B is could not finish its measurement in time
+ * -3 if the HP303B is already busy
+ * -2 if the object initialization failed
+ * -1 on other fail
+ */
+int16_t LOLIN_HP303B::measurePressureOnce(int32_t &result, uint8_t oversamplingRate)
+{
+ //start the measurement
+ int16_t ret = startMeasurePressureOnce(oversamplingRate);
+ if(ret != HP303B__SUCCEEDED)
+ {
+ return ret;
+ }
+
+ //wait until measurement is finished
+ delay(calcBusyTime(0U, m_prsOsr)/HP303B__BUSYTIME_SCALING);
+ delay(HP303B__BUSYTIME_FAILSAFE);
+
+ ret = getSingleResult(result);
+ if(ret!=HP303B__SUCCEEDED)
+ {
+ standby();
+ }
+ return ret;
+}
+
+/**
+ * starts a single pressure measurement
+ *
+ * returns: 0 on success
+ * -3 if the HP303B is already busy
+ * -2 if the object initialization failed
+ * -1 on other fail
+ */
+int16_t LOLIN_HP303B::startMeasurePressureOnce(void)
+{
+ return startMeasurePressureOnce(m_prsOsr);
+}
+
+/**
+ * starts a single pressure measurement
+ * The desired precision can be set with oversamplingRate
+ *
+ * oversamplingRate: a value from 0 to 7 that decides about the precision
+ * of the measurement
+ * If this value equals n, the HP303B will perform
+ * 2^n measurements and combine the results
+ * returns: 0 on success
+ * -3 if the HP303B is already busy
+ * -2 if the object initialization failed
+ * -1 on other fail
+ */
+int16_t LOLIN_HP303B::startMeasurePressureOnce(uint8_t oversamplingRate)
+{
+ //abort if initialization failed
+ if(m_initFail)
+ {
+ return HP303B__FAIL_INIT_FAILED;
+ }
+ //abort if device is not in idling mode
+ if(m_opMode != IDLE)
+ {
+ return HP303B__FAIL_TOOBUSY;
+ }
+ //configuration of oversampling rate, lowest measure rate to avoid conflicts
+ if(oversamplingRate != m_prsOsr)
+ {
+ if(configPressure(0U, oversamplingRate))
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ }
+ //set device to pressure measuring mode
+ return setOpMode(0U, 0U, 1U);
+}
+
+/**
+ * gets the result a single temperature or pressure measurement in °C or Pa
+ *
+ * &result: reference to a 32-Bit signed Integer value where the result will be written
+ * returns: 0 on success
+ * -4 if the HP303B is still busy
+ * -3 if the HP303B is not in command mode
+ * -2 if the object initialization failed
+ * -1 on other fail
+ */
+int16_t LOLIN_HP303B::getSingleResult(int32_t &result)
+{
+ //abort if initialization failed
+ if(m_initFail)
+ {
+ return HP303B__FAIL_INIT_FAILED;
+ }
+
+ //read finished bit for current opMode
+ int16_t rdy;
+ switch(m_opMode)
+ {
+ case CMD_TEMP: //temperature
+ rdy = readByteBitfield(HP303B__REG_INFO_TEMP_RDY);
+ break;
+ case CMD_PRS: //pressure
+ rdy = readByteBitfield(HP303B__REG_INFO_PRS_RDY);
+ break;
+ default: //HP303B not in command mode
+ return HP303B__FAIL_TOOBUSY;
+ }
+
+ //read new measurement result
+ switch(rdy)
+ {
+ case HP303B__FAIL_UNKNOWN: //could not read ready flag
+ return HP303B__FAIL_UNKNOWN;
+ case 0: //ready flag not set, measurement still in progress
+ return HP303B__FAIL_UNFINISHED;
+ case 1: //measurement ready, expected case
+ LOLIN_HP303B::Mode oldMode = m_opMode;
+ m_opMode = IDLE; //opcode was automatically reseted by HP303B
+ switch(oldMode)
+ {
+ case CMD_TEMP: //temperature
+ return getTemp(&result); //get and calculate the temperature value
+ case CMD_PRS: //pressure
+ return getPressure(&result); //get and calculate the pressure value
+ default:
+ return HP303B__FAIL_UNKNOWN; //should already be filtered above
+ }
+ }
+ return HP303B__FAIL_UNKNOWN;
+}
+
+/**
+ * starts a continuous temperature measurement
+ * The desired precision can be set with oversamplingRate
+ * The desired number of measurements per second can be set with measureRate
+ *
+ * measureRate: a value from 0 to 7 that decides about
+ * the number of measurements per second
+ * If this value equals n, the HP303B will perform
+ * 2^n measurements per second
+ * oversamplingRate: a value from 0 to 7 that decides about
+ * the precision of the measurements
+ * If this value equals m, the HP303B will perform
+ * 2^m internal measurements and combine the results
+ * to one more exact measurement
+ * returns: 0 on success
+ * -4 if measureRate or oversamplingRate is too high
+ * -3 if the HP303B is already busy
+ * -2 if the object initialization failed
+ * -1 on other fail
+ * NOTE: If measure rate is n and oversampling rate is m,
+ * the HP303B performs 2^(n+m) internal measurements per second.
+ * The HP303B cannot operate with high precision and high speed
+ * at the same time.
+ * Consult the datasheet for more information.
+ */
+int16_t LOLIN_HP303B::startMeasureTempCont(uint8_t measureRate, uint8_t oversamplingRate)
+{
+ //abort if initialization failed
+ if(m_initFail)
+ {
+ return HP303B__FAIL_INIT_FAILED;
+ }
+ //abort if device is not in idling mode
+ if(m_opMode != IDLE)
+ {
+ return HP303B__FAIL_TOOBUSY;
+ }
+ //abort if speed and precision are too high
+ if(calcBusyTime(measureRate, oversamplingRate) >= HP303B__MAX_BUSYTIME)
+ {
+ return HP303B__FAIL_UNFINISHED;
+ }
+ //update precision and measuring rate
+ if(configTemp(measureRate, oversamplingRate))
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ //enable result FIFO
+ if(writeByteBitfield(1U, HP303B__REG_INFO_FIFO_EN))
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ //Start measuring in background mode
+ if(setOpMode(1U, 1U, 0U))
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ return HP303B__SUCCEEDED;
+}
+
+
+/**
+ * starts a continuous temperature measurement
+ * The desired precision can be set with oversamplingRate
+ * The desired number of measurements per second can be set with measureRate
+ *
+ * measureRate: a value from 0 to 7 that decides about
+ * the number of measurements per second
+ * If this value equals n, the HP303B will perform
+ * 2^n measurements per second
+ * oversamplingRate: a value from 0 to 7 that decides about the precision
+ * of the measurements
+ * If this value equals m, the HP303B will perform
+ * 2^m internal measurements
+ * and combine the results to one more exact measurement
+ * returns: 0 on success
+ * -4 if measureRate or oversamplingRate is too high
+ * -3 if the HP303B is already busy
+ * -2 if the object initialization failed
+ * -1 on other fail
+ * NOTE: If measure rate is n and oversampling rate is m,
+ * the HP303B performs 2^(n+m) internal measurements per second.
+ * The HP303B cannot operate with high precision and high speed
+ * at the same time.
+ * Consult the datasheet for more information.
+ */
+int16_t LOLIN_HP303B::startMeasurePressureCont(uint8_t measureRate, uint8_t oversamplingRate)
+{
+ //abort if initialization failed
+ if(m_initFail)
+ {
+ return HP303B__FAIL_INIT_FAILED;
+ }
+ //abort if device is not in idling mode
+ if(m_opMode != IDLE)
+ {
+ return HP303B__FAIL_TOOBUSY;
+ }
+ //abort if speed and precision are too high
+ if(calcBusyTime(measureRate, oversamplingRate) >= HP303B__MAX_BUSYTIME)
+ {
+ return HP303B__FAIL_UNFINISHED;
+ }
+ //update precision and measuring rate
+ if(configPressure(measureRate, oversamplingRate))
+ return HP303B__FAIL_UNKNOWN;
+ //enable result FIFO
+ if(writeByteBitfield(1U, HP303B__REG_INFO_FIFO_EN))
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ //Start measuring in background mode
+ if(setOpMode(1U, 0U, 1U))
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ return HP303B__SUCCEEDED;
+}
+
+/**
+ * starts a continuous temperature and pressure measurement
+ * The desired precision can be set with tempOsr and prsOsr
+ * The desired number of measurements per second can be set with tempMr and prsMr
+ *
+ * tempMr measure rate for temperature
+ * tempOsr oversampling rate for temperature
+ * prsMr measure rate for pressure
+ * prsOsr oversampling rate for pressure
+ * returns: 0 on success
+ * -4 if precision or speed is too high
+ * -3 if the HP303B is already busy
+ * -2 if the object initialization failed
+ * -1 on other fail
+ * NOTE: High precision and speed for both temperature and pressure
+ * can not be reached at the same time.
+ * Estimated time for temperature and pressure measurement
+ * is the sum of both values.
+ * This sum must not be more than 1 second.
+ * Consult the datasheet for more information.
+ */
+int16_t LOLIN_HP303B::startMeasureBothCont(uint8_t tempMr,
+ uint8_t tempOsr,
+ uint8_t prsMr,
+ uint8_t prsOsr)
+{
+ //abort if initialization failed
+ if(m_initFail)
+ {
+ return HP303B__FAIL_INIT_FAILED;
+ }
+ //abort if device is not in idling mode
+ if(m_opMode!=IDLE)
+ {
+ return HP303B__FAIL_TOOBUSY;
+ }
+ //abort if speed and precision are too high
+ if(calcBusyTime(tempMr, tempOsr) + calcBusyTime(prsMr, prsOsr)>=HP303B__MAX_BUSYTIME)
+ {
+ return HP303B__FAIL_UNFINISHED;
+ }
+ //update precision and measuring rate
+ if(configTemp(tempMr, tempOsr))
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ //update precision and measuring rate
+ if(configPressure(prsMr, prsOsr))
+ return HP303B__FAIL_UNKNOWN;
+ //enable result FIFO
+ if(writeByteBitfield(1U, HP303B__REG_INFO_FIFO_EN))
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ //Start measuring in background mode
+ if(setOpMode(1U, 1U, 1U))
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ return HP303B__SUCCEEDED;
+}
+
+/**
+ * Gets the results from continuous measurements and writes them to given arrays
+ *
+ * *tempBuffer: The start address of the buffer where the temperature results
+ * are written
+ * If this is NULL, no temperature results will be written out
+ * &tempCount: This has to be a reference to a number which contains
+ * the size of the buffer for temperature results.
+ * When the function ends, it will contain
+ * the number of bytes written to the buffer
+ * *prsBuffer: The start address of the buffer where the pressure results
+ * are written
+ * If this is NULL, no pressure results will be written out
+ * &prsCount: This has to be a reference to a number which contains
+ * the size of the buffer for pressure results.
+ * When the function ends, it will contain
+ * the number of bytes written to the buffer
+ * returns: 0 on success
+ * -3 if HP303B is not in background mode
+ * -2 if the object initialization failed
+ * -1 on other fail
+ */
+int16_t LOLIN_HP303B::getContResults(int32_t *tempBuffer,
+ uint8_t &tempCount,
+ int32_t *prsBuffer,
+ uint8_t &prsCount)
+{
+ if(m_initFail)
+ {
+ return HP303B__FAIL_INIT_FAILED;
+ }
+ //abort if device is not in background mode
+ if(!(m_opMode & INVAL_OP_CONT_NONE))
+ {
+ return HP303B__FAIL_TOOBUSY;
+ }
+
+ //prepare parameters for buffer length and count
+ uint8_t tempLen = tempCount;
+ uint8_t prsLen = prsCount;
+ tempCount = 0U;
+ prsCount = 0U;
+
+ //while FIFO is not empty
+ while(readByteBitfield(HP303B__REG_INFO_FIFO_EMPTY) == 0)
+ {
+ int32_t result;
+ //read next result from FIFO
+ int16_t type = getFIFOvalue(&result);
+ switch(type)
+ {
+ case 0: //temperature
+ //calculate compensated pressure value
+ result = calcTemp(result);
+ //if buffer exists and is not full
+ //write result to buffer and increase temperature result counter
+ if(tempBuffer != NULL)
+ {
+ if(tempCount> HP303B__REG_SHIFT_INT_EN_FIFO;
+ tempReady &= HP303B__REG_MASK_INT_EN_TEMP >> HP303B__REG_SHIFT_INT_EN_TEMP;
+ prsReady &= HP303B__REG_MASK_INT_EN_PRS >> HP303B__REG_SHIFT_INT_EN_PRS;
+ //read old value from register
+ int16_t regData = readByte(HP303B__REG_ADR_INT_EN_FIFO);
+ if(regData <0)
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ uint8_t toWrite = (uint8_t)regData;
+ //update FIFO enable bit
+ toWrite &= ~HP303B__REG_MASK_INT_EN_FIFO; //clear bit
+ toWrite |= fifoFull << HP303B__REG_SHIFT_INT_EN_FIFO; //set new bit
+ //update TempReady enable bit
+ toWrite &= ~HP303B__REG_MASK_INT_EN_TEMP;
+ toWrite |= tempReady << HP303B__REG_SHIFT_INT_EN_TEMP;
+ //update PrsReady enable bit
+ toWrite &= ~HP303B__REG_MASK_INT_EN_PRS;
+ toWrite |= prsReady << HP303B__REG_SHIFT_INT_EN_PRS;
+ //write updated value to register
+ return writeByte(HP303B__REG_ADR_INT_EN_FIFO, toWrite);
+}
+
+/**
+ * Gets the interrupt status flag of the FIFO
+ *
+ * Returns: 1 if the FIFO is full and caused an interrupt
+ * 0 if the FIFO is not full or FIFO interrupt is disabled
+ * -1 on fail
+ */
+int16_t LOLIN_HP303B::getIntStatusFifoFull(void)
+{
+ return readByteBitfield(HP303B__REG_INFO_INT_FLAG_FIFO);
+}
+
+/**
+ * Gets the interrupt status flag that indicates a finished temperature measurement
+ *
+ * Returns: 1 if a finished temperature measurement caused an interrupt
+ * 0 if there is no finished temperature measurement
+ * or interrupts are disabled
+ * -1 on fail
+ */
+int16_t LOLIN_HP303B::getIntStatusTempReady(void)
+{
+ return readByteBitfield(HP303B__REG_INFO_INT_FLAG_TEMP);
+}
+
+/**
+ * Gets the interrupt status flag that indicates a finished pressure measurement
+ *
+ * Returns: 1 if a finished pressure measurement caused an interrupt
+ * 0 if there is no finished pressure measurement
+ * or interrupts are disabled
+ * -1 on fail
+ */
+int16_t LOLIN_HP303B::getIntStatusPrsReady(void)
+{
+ return readByteBitfield(HP303B__REG_INFO_INT_FLAG_PRS);
+}
+
+/**
+ * Function to fix a hardware problem on some devices
+ * You have this problem if you measure a temperature which is too high (e.g. 60°C when temperature is around 20°C)
+ * Call correctTemp() directly after begin() to fix this issue
+ */
+int16_t LOLIN_HP303B::correctTemp(void)
+{
+ if(m_initFail)
+ {
+ return HP303B__FAIL_INIT_FAILED;
+ }
+ writeByte(0x0E, 0xA5);
+ writeByte(0x0F, 0x96);
+ writeByte(0x62, 0x02);
+ writeByte(0x0E, 0x00);
+ writeByte(0x0F, 0x00);
+
+ //perform a first temperature measurement (again)
+ //the most recent temperature will be saved internally
+ //and used for compensation when calculating pressure
+ int32_t trash;
+ measureTempOnce(trash);
+
+ return HP303B__SUCCEEDED;
+}
+
+
+
+//////// Declaration of private functions starts here ////////
+
+
+/**
+ * Initializes the sensor.
+ * This function has to be called from begin()
+ * and requires a valid bus initialization.
+ */
+void LOLIN_HP303B::init(void)
+{
+ int16_t prodId = readByteBitfield(HP303B__REG_INFO_PROD_ID);
+ if(prodId != HP303B__PROD_ID)
+ {
+ //Connected device is not a HP303B
+ m_initFail = 1U;
+ return;
+ }
+ m_productID = prodId;
+
+ int16_t revId = readByteBitfield(HP303B__REG_INFO_REV_ID);
+ if(revId < 0)
+ {
+ m_initFail = 1U;
+ return;
+ }
+ m_revisionID = revId;
+
+ //find out which temperature sensor is calibrated with coefficients...
+ int16_t sensor = readByteBitfield(HP303B__REG_INFO_TEMP_SENSORREC);
+ if(sensor < 0)
+ {
+ m_initFail = 1U;
+ return;
+ }
+
+ //...and use this sensor for temperature measurement
+ m_tempSensor = sensor;
+ if(writeByteBitfield((uint8_t)sensor, HP303B__REG_INFO_TEMP_SENSOR) < 0)
+ {
+ m_initFail = 1U;
+ return;
+ }
+
+ //read coefficients
+ if(readcoeffs() < 0)
+ {
+ m_initFail = 1U;
+ return;
+ }
+
+ //set to standby for further configuration
+ standby();
+
+ //set measurement precision and rate to standard values;
+ configTemp(HP303B__TEMP_STD_MR, HP303B__TEMP_STD_OSR);
+ configPressure(HP303B__PRS_STD_MR, HP303B__PRS_STD_OSR);
+
+ //perform a first temperature measurement
+ //the most recent temperature will be saved internally
+ //and used for compensation when calculating pressure
+ int32_t trash;
+ measureTempOnce(trash);
+
+ //make sure the HP303B is in standby after initialization
+ standby();
+
+ // Fix IC with a fuse bit problem, which lead to a wrong temperature
+ // Should not affect ICs without this problem
+ correctTemp();
+}
+
+
+/**
+ * reads the compensation coefficients from the HP303B
+ * this is called once from init(), which is called from begin()
+ *
+ * returns: 0 on success, -1 on fail
+ */
+int16_t LOLIN_HP303B::readcoeffs(void)
+{
+ uint8_t buffer[HP303B__REG_LEN_COEF];
+ //read COEF registers to buffer
+ int16_t ret = readBlock(HP303B__REG_ADR_COEF,
+ HP303B__REG_LEN_COEF,
+ buffer);
+ //abort if less than REG_LEN_COEF bytes were read
+ if(ret < HP303B__REG_LEN_COEF)
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+
+ //compose coefficients from buffer content
+ m_c0Half = ((uint32_t)buffer[0] << 4)
+ | (((uint32_t)buffer[1] >> 4) & 0x0F);
+ //this construction recognizes non-32-bit negative numbers
+ //and converts them to 32-bit negative numbers with 2's complement
+ if(m_c0Half & ((uint32_t)1 << 11))
+ {
+ m_c0Half -= (uint32_t)1 << 12;
+ }
+ //c0 is only used as c0*0.5, so c0_half is calculated immediately
+ m_c0Half = m_c0Half / 2U;
+
+ //now do the same thing for all other coefficients
+ m_c1 = (((uint32_t)buffer[1] & 0x0F) << 8) | (uint32_t)buffer[2];
+ if(m_c1 & ((uint32_t)1 << 11))
+ {
+ m_c1 -= (uint32_t)1 << 12;
+ }
+
+ m_c00 = ((uint32_t)buffer[3] << 12)
+ | ((uint32_t)buffer[4] << 4)
+ | (((uint32_t)buffer[5] >> 4) & 0x0F);
+ if(m_c00 & ((uint32_t)1 << 19))
+ {
+ m_c00 -= (uint32_t)1 << 20;
+ }
+
+ m_c10 = (((uint32_t)buffer[5] & 0x0F) << 16)
+ | ((uint32_t)buffer[6] << 8)
+ | (uint32_t)buffer[7];
+ if(m_c10 & ((uint32_t)1<<19))
+ {
+ m_c10 -= (uint32_t)1 << 20;
+ }
+
+ m_c01 = ((uint32_t)buffer[8] << 8)
+ | (uint32_t)buffer[9];
+ if(m_c01 & ((uint32_t)1 << 15))
+ {
+ m_c01 -= (uint32_t)1 << 16;
+ }
+
+ m_c11 = ((uint32_t)buffer[10] << 8)
+ | (uint32_t)buffer[11];
+ if(m_c11 & ((uint32_t)1 << 15))
+ {
+ m_c11 -= (uint32_t)1 << 16;
+ }
+
+ m_c20 = ((uint32_t)buffer[12] << 8)
+ | (uint32_t)buffer[13];
+ if(m_c20 & ((uint32_t)1 << 15))
+ {
+ m_c20 -= (uint32_t)1 << 16;
+ }
+
+ m_c21 = ((uint32_t)buffer[14] << 8)
+ | (uint32_t)buffer[15];
+ if(m_c21 & ((uint32_t)1 << 15))
+ {
+ m_c21 -= (uint32_t)1 << 16;
+ }
+
+ m_c30 = ((uint32_t)buffer[16] << 8)
+ | (uint32_t)buffer[17];
+ if(m_c30 & ((uint32_t)1 << 15))
+ {
+ m_c30 -= (uint32_t)1 << 16;
+ }
+
+ return HP303B__SUCCEEDED;
+}
+
+/**
+ * Sets the Operation Mode of the HP303B
+ *
+ * background: determines the general behavior of the HP303B
+ * 0 enables command mode (only measure on commands)
+ * 1 enables background mode (continuous work in background)
+ * temperature: set 1 to measure temperature
+ * pressure: set 1 to measure pressure
+ * return: 0 on success, -1 on fail
+ *
+ * NOTE!
+ * You cannot set background to 1 without setting temperature and pressure
+ * You cannot set both temperature and pressure when background mode is disabled
+ */
+int16_t LOLIN_HP303B::setOpMode(uint8_t background, uint8_t temperature, uint8_t pressure)
+{
+ uint8_t opMode = (background & HP303B__LSB) << 2U
+ | (temperature & HP303B__LSB) << 1U
+ | (pressure & HP303B__LSB);
+ return setOpMode(opMode);
+}
+
+
+/**
+ * Sets the Operation Mode of the HP303B
+ *
+ * opMode: the new OpMode that has to be set
+ * return: 0 on success, -1 on fail
+ *
+ * NOTE!
+ * You cannot set background to 1 without setting temperature and pressure
+ * You cannot set both temperature and pressure when background mode is disabled
+ */
+int16_t LOLIN_HP303B::setOpMode(uint8_t opMode)
+{
+ //Filter irrelevant bits
+ opMode &= HP303B__REG_MASK_OPMODE >> HP303B__REG_SHIFT_OPMODE;
+ //Filter invalid OpModes
+ if(opMode == INVAL_OP_CMD_BOTH || opMode == INVAL_OP_CONT_NONE)
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ //Set OpMode
+ if(writeByte(HP303B__REG_ADR_OPMODE, opMode))
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ m_opMode = (LOLIN_HP303B::Mode)opMode;
+ return HP303B__SUCCEEDED;
+}
+
+/**
+ * Configures temperature measurement
+ *
+ * tempMr: the new measure rate for temperature
+ * This can be a value from 0U to 7U.
+ * Actual measure rate will be 2^tempMr,
+ * so this will be a value from 1 to 128.
+ * tempOsr: the new oversampling rate for temperature
+ * This can be a value from 0U to 7U.
+ * Actual measure rate will be 2^tempOsr,
+ * so this will be a value from 1 to 128.
+ * returns: 0 normally or -1 on fail
+ */
+int16_t LOLIN_HP303B::configTemp(uint8_t tempMr, uint8_t tempOsr)
+{
+ //mask parameters
+ tempMr &= HP303B__REG_MASK_TEMP_MR >> HP303B__REG_SHIFT_TEMP_MR;
+ tempOsr &= HP303B__REG_MASK_TEMP_OSR >> HP303B__REG_SHIFT_TEMP_OSR;
+
+ //set config register according to parameters
+ uint8_t toWrite = tempMr << HP303B__REG_SHIFT_TEMP_MR;
+ toWrite |= tempOsr << HP303B__REG_SHIFT_TEMP_OSR;
+ //using recommended temperature sensor
+ toWrite |= HP303B__REG_MASK_TEMP_SENSOR
+ & (m_tempSensor << HP303B__REG_SHIFT_TEMP_SENSOR);
+ int16_t ret = writeByte(HP303B__REG_ADR_TEMP_MR, toWrite);
+ //abort immediately on fail
+ if(ret != HP303B__SUCCEEDED)
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+
+ //set TEMP SHIFT ENABLE if oversampling rate higher than eight(2^3)
+ if(tempOsr > HP303B__OSR_SE)
+ {
+ ret=writeByteBitfield(1U, HP303B__REG_INFO_TEMP_SE);
+ }
+ else
+ {
+ ret=writeByteBitfield(0U, HP303B__REG_INFO_TEMP_SE);
+ }
+
+ if(ret == HP303B__SUCCEEDED)
+ { //save new settings
+ m_tempMr = tempMr;
+ m_tempOsr = tempOsr;
+ }
+ else
+ {
+ //try to rollback on fail avoiding endless recursion
+ //this is to make sure that shift enable and oversampling rate
+ //are always consistent
+ if(tempMr != m_tempMr || tempOsr != m_tempOsr)
+ {
+ configTemp(m_tempMr, m_tempOsr);
+ }
+ }
+ return ret;
+}
+
+/**
+ * Configures pressure measurement
+ *
+ * prsMr: the new measure rate for pressure
+ * This can be a value from 0U to 7U.
+ * Actual measure rate will be 2^prs_mr,
+ * so this will be a value from 1 to 128.
+ * prsOs: the new oversampling rate for temperature
+ * This can be a value from 0U to 7U.
+ * Actual measure rate will be 2^prsOsr,
+ * so this will be a value from 1 to 128.
+ * returns: 0 normally or -1 on fail
+ */
+int16_t LOLIN_HP303B::configPressure(uint8_t prsMr, uint8_t prsOsr)
+{
+ //mask parameters
+ prsMr &= HP303B__REG_MASK_PRS_MR >> HP303B__REG_SHIFT_PRS_MR;
+ prsOsr &= HP303B__REG_MASK_PRS_OSR >> HP303B__REG_SHIFT_PRS_OSR;
+
+ //set config register according to parameters
+ uint8_t toWrite = prsMr << HP303B__REG_SHIFT_PRS_MR;
+ toWrite |= prsOsr << HP303B__REG_SHIFT_PRS_OSR;
+ int16_t ret = writeByte(HP303B__REG_ADR_PRS_MR, toWrite);
+ //abort immediately on fail
+ if(ret != HP303B__SUCCEEDED)
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+
+ //set PM SHIFT ENABLE if oversampling rate higher than eight(2^3)
+ if(prsOsr > HP303B__OSR_SE)
+ {
+ ret = writeByteBitfield(1U, HP303B__REG_INFO_PRS_SE);
+ }
+ else
+ {
+ ret = writeByteBitfield(0U, HP303B__REG_INFO_PRS_SE);
+ }
+
+ if(ret == HP303B__SUCCEEDED)
+ { //save new settings
+ m_prsMr = prsMr;
+ m_prsOsr = prsOsr;
+ }
+ else
+ { //try to rollback on fail avoiding endless recursion
+ //this is to make sure that shift enable and oversampling rate
+ //are always consistent
+ if(prsMr != m_prsMr || prsOsr != m_prsOsr)
+ {
+ configPressure(m_prsMr, m_prsOsr);
+ }
+ }
+ return ret;
+}
+
+/**
+ * calculates the time that the HP303B needs for 2^mr measurements
+ * with an oversampling rate of 2^osr
+ *
+ * mr: Measure rate for temperature or pressure
+ * osr: Oversampling rate for temperature or pressure
+ * returns: time that the HP303B needs for this measurement
+ * a value of 10000 equals 1 second
+ * NOTE! The measurement time for temperature and pressure
+ * in sum must not be more than 1 second!
+ * Timing behavior of pressure and temperature sensors
+ * can be considered as equal.
+ */
+uint16_t LOLIN_HP303B::calcBusyTime(uint16_t mr, uint16_t osr)
+{
+ //mask parameters first
+ mr &= HP303B__REG_MASK_TEMP_MR >> HP303B__REG_SHIFT_TEMP_MR;
+ osr &= HP303B__REG_MASK_TEMP_OSR >> HP303B__REG_SHIFT_TEMP_OSR;
+ //formula from datasheet (optimized)
+ return ((uint32_t)20U << mr) + ((uint32_t)16U << (osr + mr));
+}
+
+/**
+ * Gets the next temperature measurement result in degrees of Celsius
+ *
+ * result: address where the result will be written
+ * returns: 0 on success
+ * -1 on fail;
+ */
+int16_t LOLIN_HP303B::getTemp(int32_t *result)
+{
+ uint8_t buffer[3] = {0};
+ //read raw pressure data to buffer
+
+ int16_t i = readBlock(HP303B__REG_ADR_TEMP,
+ HP303B__REG_LEN_TEMP,
+ buffer);
+ if(i != HP303B__REG_LEN_TEMP)
+ {
+ //something went wrong
+ return HP303B__FAIL_UNKNOWN;
+ }
+
+ //compose raw temperature value from buffer
+ int32_t temp = (uint32_t)buffer[0] << 16
+ | (uint32_t)buffer[1] << 8
+ | (uint32_t)buffer[2];
+ //recognize non-32-bit negative numbers
+ //and convert them to 32-bit negative numbers using 2's complement
+ if(temp & ((uint32_t)1 << 23))
+ {
+ temp -= (uint32_t)1 << 24;
+ }
+
+ //return temperature
+ *result = calcTemp(temp);
+ return HP303B__SUCCEEDED;
+}
+
+/**
+ * Gets the next pressure measurement result in Pa
+ *
+ * result: address where the result will be written
+ * returns: 0 on success
+ * -1 on fail;
+ */
+int16_t LOLIN_HP303B::getPressure(int32_t *result)
+{
+ uint8_t buffer[3] = {0};
+ //read raw pressure data to buffer
+ int16_t i = readBlock(HP303B__REG_ADR_PRS,
+ HP303B__REG_LEN_PRS,
+ buffer);
+ if(i != HP303B__REG_LEN_PRS)
+ {
+ //something went wrong
+ //negative pressure is not allowed
+ return HP303B__FAIL_UNKNOWN;
+ }
+
+ //compose raw pressure value from buffer
+ int32_t prs = (uint32_t)buffer[0] << 16
+ | (uint32_t)buffer[1] << 8
+ | (uint32_t)buffer[2];
+ //recognize non-32-bit negative numbers
+ //and convert them to 32-bit negative numbers using 2's complement
+ if(prs & ((uint32_t)1 << 23))
+ {
+ prs -= (uint32_t)1 << 24;
+ }
+
+ *result = calcPressure(prs);
+ return HP303B__SUCCEEDED;
+}
+
+/**
+ * reads the next raw value from the HP303B FIFO
+ *
+ * value: address where the value will be written
+ * returns: -1 on fail
+ * 0 if result is a temperature raw value
+ * 1 if result is a pressure raw value
+ */
+int16_t LOLIN_HP303B::getFIFOvalue(int32_t* value)
+{
+ //abort on invalid argument
+ if(value == NULL)
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+
+ uint8_t buffer[HP303B__REG_LEN_PRS] = {0};
+ //always read from pressure raw value register
+ int16_t i = readBlock(HP303B__REG_ADR_PRS,
+ HP303B__REG_LEN_PRS,
+ buffer);
+ if(i != HP303B__REG_LEN_PRS)
+ {
+ //something went wrong
+ //return error code
+ return HP303B__FAIL_UNKNOWN;
+ }
+ //compose raw pressure value from buffer
+ *value = (uint32_t)buffer[0] << 16
+ | (uint32_t)buffer[1] << 8
+ | (uint32_t)buffer[2];
+ //recognize non-32-bit negative numbers
+ //and convert them to 32-bit negative numbers using 2's complement
+ if(*value & ((uint32_t)1 << 23))
+ {
+ *value -= (uint32_t)1 << 24;
+ }
+
+ //least significant bit shows measurement type
+ return buffer[2] & HP303B__LSB;
+}
+
+/**
+ * Calculates a scaled and compensated pressure value from raw data
+ * raw: raw temperature value read from HP303B
+ * returns: temperature value in °C
+ */
+int32_t LOLIN_HP303B::calcTemp(int32_t raw)
+{
+ double temp = raw;
+
+ //scale temperature according to scaling table and oversampling
+ temp /= scaling_facts[m_tempOsr];
+
+ //update last measured temperature
+ //it will be used for pressure compensation
+ m_lastTempScal = temp;
+
+ //Calculate compensated temperature
+ temp = m_c0Half + m_c1 * temp;
+
+ //return temperature
+ return (int32_t)temp;
+}
+
+/**
+ * Calculates a scaled and compensated pressure value from raw data
+ * raw: raw pressure value read from HP303B
+ * returns: pressure value in Pa
+ */
+int32_t LOLIN_HP303B::calcPressure(int32_t raw)
+{
+ double prs = raw;
+
+ //scale pressure according to scaling table and oversampling
+ prs /= scaling_facts[m_prsOsr];
+
+ //Calculate compensated pressure
+ prs = m_c00
+ + prs * (m_c10 + prs * (m_c20 + prs * m_c30))
+ + m_lastTempScal * (m_c01 + prs * (m_c11 + prs * m_c21));
+
+ //return pressure
+ return (int32_t)prs;
+}
+
+/**
+ * reads a byte from HP303B
+ *
+ * regAdress: Address that has to be read
+ * returns: register content or -1 on fail
+ */
+int16_t LOLIN_HP303B::readByte(uint8_t regAddress)
+{
+ //delegate to specialized function if HP303B is connected via SPI
+ if(m_SpiI2c==0)
+ {
+ return readByteSPI(regAddress);
+ }
+
+ m_i2cbus->beginTransmission(m_slaveAddress);
+ m_i2cbus->write(regAddress);
+ m_i2cbus->endTransmission(0);
+ //request 1 byte from slave
+ if(m_i2cbus->requestFrom(m_slaveAddress, 1U, 1U) > 0)
+ {
+ return m_i2cbus->read(); //return this byte on success
+ }
+ else
+ {
+ return HP303B__FAIL_UNKNOWN; //if 0 bytes were read successfully
+ }
+}
+
+/**
+ * reads a byte from HP303B via SPI
+ * this function is automatically called by readByte
+ * if HP303B is connected via SPI
+ *
+ * regAdress: Address that has to be read
+ * returns: register content or -1 on fail
+ */
+int16_t LOLIN_HP303B::readByteSPI(uint8_t regAddress)
+{
+ //this function is only made for communication via SPI
+ if(m_SpiI2c != 0)
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ //mask regAddress
+ regAddress &= ~HP303B__SPI_RW_MASK;
+ //reserve and initialize bus
+ m_spibus->beginTransaction(SPISettings(HP303B__SPI_MAX_FREQ,
+ MSBFIRST,
+ SPI_MODE3));
+ //enable ChipSelect for HP303B
+ digitalWrite(m_chipSelect, LOW);
+ //send address with read command to HP303B
+ m_spibus->transfer(regAddress | HP303B__SPI_READ_CMD);
+ //receive register content from HP303B
+ uint8_t ret = m_spibus->transfer(0xFF); //send a dummy byte while receiving
+ //disable ChipSelect for HP303B
+ digitalWrite(m_chipSelect, HIGH);
+ //close current SPI transaction
+ m_spibus->endTransaction();
+ //return received data
+ return ret;
+}
+
+/**
+ * reads a block from HP303B
+ *
+ * regAdress: Address that has to be read
+ * length: Length of data block
+ * buffer: Buffer where data will be stored
+ * returns: number of bytes that have been read successfully
+ * NOTE! This is not always equal to length
+ * due to rx-Buffer overflow etc.
+ */
+int16_t LOLIN_HP303B::readBlock(uint8_t regAddress, uint8_t length, uint8_t *buffer)
+{
+ //delegate to specialized function if HP303B is connected via SPI
+ if(m_SpiI2c == 0)
+ {
+ return readBlockSPI(regAddress, length, buffer);
+ }
+ //do not read if there is no buffer
+ if(buffer == NULL)
+ {
+ return 0; //0 bytes read successfully
+ }
+
+ m_i2cbus->beginTransmission(m_slaveAddress);
+ m_i2cbus->write(regAddress);
+ m_i2cbus->endTransmission(0);
+ //request length bytes from slave
+ int16_t ret = m_i2cbus->requestFrom(m_slaveAddress, length, 1U);
+ //read all received bytes to buffer
+ for(int16_t count = 0; count < ret; count++)
+ {
+ buffer[count] = m_i2cbus->read();
+ }
+ return ret;
+}
+
+/**
+ * reads a block from HP303B via SPI
+ *
+ * regAdress: Address that has to be read
+ * length: Length of data block
+ * readbuffer: Buffer where data will be stored
+ * returns: number of bytes that have been read successfully
+ * NOTE! This is not always equal to length
+ * due to rx-Buffer overflow etc.
+ */
+int16_t LOLIN_HP303B::readBlockSPI(uint8_t regAddress, uint8_t length, uint8_t *buffer)
+{
+ //this function is only made for communication via SPI
+ if(m_SpiI2c != 0)
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ //do not read if there is no buffer
+ if(buffer == NULL)
+ {
+ return 0; //0 bytes were read successfully
+ }
+ //mask regAddress
+ regAddress &= ~HP303B__SPI_RW_MASK;
+ //reserve and initialize bus
+ m_spibus->beginTransaction(SPISettings(HP303B__SPI_MAX_FREQ,
+ MSBFIRST,
+ SPI_MODE3));
+ //enable ChipSelect for HP303B
+ digitalWrite(m_chipSelect, LOW);
+ //send address with read command to HP303B
+ m_spibus->transfer(regAddress | HP303B__SPI_READ_CMD);
+
+ //receive register contents from HP303B
+ for(uint8_t count = 0; count < length; count++)
+ {
+ buffer[count] = m_spibus->transfer(0xFF);//send a dummy byte while receiving
+ }
+
+ //disable ChipSelect for HP303B
+ digitalWrite(m_chipSelect, HIGH);
+ //close current SPI transaction
+ m_spibus->endTransaction();
+ //return received data
+ return length;
+}
+
+/**
+ * writes a given byte to a given register of HP303B without checking
+ *
+ * regAdress: Address of the register that has to be updated
+ * data: Byte that will be written to the register
+ * return: 0 if byte was written successfully
+ * or -1 on fail
+ */
+int16_t LOLIN_HP303B::writeByte(uint8_t regAddress, uint8_t data)
+{
+ return writeByte(regAddress, data, 0U);
+}
+
+/**
+ * writes a given byte to a given register of HP303B
+ *
+ * regAdress: Address of the register that has to be updated
+ * data: Byte that will be written to the register
+ * check: If this is true, register content will be read after writing
+ * to check if update was successful
+ * return: 0 if byte was written successfully
+ * or -1 on fail
+ */
+int16_t LOLIN_HP303B::writeByte(uint8_t regAddress, uint8_t data, uint8_t check)
+{
+ //delegate to specialized function if HP303B is connected via SPI
+ if(m_SpiI2c==0)
+ {
+ return writeByteSpi(regAddress, data, check);
+ }
+ m_i2cbus->beginTransmission(m_slaveAddress);
+ m_i2cbus->write(regAddress); //Write Register number to buffer
+ m_i2cbus->write(data); //Write data to buffer
+ if(m_i2cbus->endTransmission() != 0) //Send buffer content to slave
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ else
+ {
+ if(check == 0) return 0; //no checking
+ if(readByte(regAddress) == data) //check if desired by calling function
+ {
+ return HP303B__SUCCEEDED;
+ }
+ else
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ }
+}
+
+/**
+ * writes a given byte to a given register of HP303B via SPI
+ *
+ * regAdress: Address of the register that has to be updated
+ * data: Byte that will be written to the register
+ * check: If this is true, register content will be read after writing
+ * to check if update was successful
+ * return: 0 if byte was written successfully
+ * or -1 on fail
+ */
+int16_t LOLIN_HP303B::writeByteSpi(uint8_t regAddress, uint8_t data, uint8_t check)
+{
+ //this function is only made for communication via SPI
+ if(m_SpiI2c != 0)
+ {
+ return HP303B__FAIL_UNKNOWN;
+ }
+ //mask regAddress
+ regAddress &= ~HP303B__SPI_RW_MASK;
+ //reserve and initialize bus
+ m_spibus->beginTransaction(SPISettings(HP303B__SPI_MAX_FREQ,
+ MSBFIRST,
+ SPI_MODE3));
+ //enable ChipSelect for HP303B
+ digitalWrite(m_chipSelect, LOW);
+ //send address with read command to HP303B
+ m_spibus->transfer(regAddress | HP303B__SPI_WRITE_CMD);
+
+ //write register content from HP303B
+ m_spibus->transfer(data);
+
+ //disable ChipSelect for HP303B
+ digitalWrite(m_chipSelect, HIGH);
+ //close current SPI transaction
+ m_spibus->endTransaction();
+
+ //check if necessary
+ if(check == 0)
+ {
+ //no checking necessary
+ return HP303B__SUCCEEDED;
+ }
+ //checking necessary
+ if(readByte(regAddress) == data)
+ {
+ //check passed
+ return HP303B__SUCCEEDED;
+ }
+ else
+ {
+ //check failed
+ return HP303B__FAIL_UNKNOWN;
+ }
+}
+
+/**
+ * updates some given bits of a given register of HP303B without checking
+ *
+ * regAdress: Address of the register that has to be updated
+ * data: BitValues that will be written to the register
+ * shift: Amount of bits the data byte is shifted (left) before being masked
+ * mask: Masks the bits of the register that have to be updated
+ * Bits with value 1 are updated
+ * Bits with value 0 are not changed
+ * return: 0 if byte was written successfully
+ * or -1 on fail
+ */
+int16_t LOLIN_HP303B::writeByteBitfield(uint8_t data,
+ uint8_t regAddress,
+ uint8_t mask,
+ uint8_t shift)
+{
+ return writeByteBitfield(data, regAddress, mask, shift, 0U);
+}
+
+/**
+ * updates some given bits of a given register of HP303B
+ *
+ * regAdress: Address of the register that has to be updated
+ * data: BitValues that will be written to the register
+ * shift: Amount of bits the data byte is shifted (left) before being masked
+ * mask: Masks the bits of the register that have to be updated
+ * Bits with value 1 are updated
+ * Bits with value 0 are not changed
+ * check: enables/disables check after writing
+ * 0 disables check
+ * if check fails, -1 will be returned
+ * return: 0 if byte was written successfully
+ * or -1 on fail
+ */
+int16_t LOLIN_HP303B::writeByteBitfield(uint8_t data,
+ uint8_t regAddress,
+ uint8_t mask,
+ uint8_t shift,
+ uint8_t check)
+{
+ int16_t old = readByte(regAddress);
+ if(old < 0)
+ {
+ //fail while reading
+ return old;
+ }
+ return writeByte(regAddress, ((uint8_t)old & ~mask)|((data << shift) & mask), check);
+}
+
+/**
+ * reads some given bits of a given register of HP303B
+ *
+ * regAdress: Address of the register that has to be updated
+ * mask: Masks the bits of the register that have to be updated
+ * Bits masked with value 1 are read
+ * Bits masked with value 0 are set 0
+ * shift: Amount of bits the data byte is shifted (right) after being masked
+ * return: read and processed bits
+ * or -1 on fail
+ */
+int16_t LOLIN_HP303B::readByteBitfield(uint8_t regAddress, uint8_t mask, uint8_t shift)
+{
+ int16_t ret = readByte(regAddress);
+ if(ret<0)
+ {
+ return ret;
+ }
+ return (((uint8_t)ret) & mask) >> shift;
+}
diff --git a/lib/LOLIN_HP303B/src/LOLIN_HP303B.h b/lib/LOLIN_HP303B/src/LOLIN_HP303B.h
new file mode 100644
index 000000000..f9c54e356
--- /dev/null
+++ b/lib/LOLIN_HP303B/src/LOLIN_HP303B.h
@@ -0,0 +1,144 @@
+#ifndef __LOLIN_HP303B_H
+#define __LOLIN_HP303B_H
+
+#if ARDUINO >= 100
+#include "Arduino.h"
+#else
+#include "WProgram.h"
+#endif
+
+#include
+#include
+#include "util/hp303b_consts.h"
+
+class LOLIN_HP303B
+{
+public:
+ //constructor
+ LOLIN_HP303B(void);
+ //destructor
+ ~LOLIN_HP303B(void);
+ //begin
+ void begin(TwoWire &bus, uint8_t slaveAddress);
+ void begin(uint8_t slaveAddress=HP303B__STD_SLAVE_ADDRESS);
+ void begin(SPIClass &bus, int32_t chipSelect);
+ void begin(SPIClass &bus, int32_t chipSelect, uint8_t threeWire);
+ //end
+ void end(void);
+
+ //general
+ uint8_t getProductId(void);
+ uint8_t getRevisionId(void);
+
+ //Idle Mode
+ int16_t standby(void);
+
+ //Command Mode
+ int16_t measureTempOnce(int32_t &result);
+ int16_t measureTempOnce(int32_t &result, uint8_t oversamplingRate);
+ int16_t startMeasureTempOnce(void);
+ int16_t startMeasureTempOnce(uint8_t oversamplingRate);
+ int16_t measurePressureOnce(int32_t &result);
+ int16_t measurePressureOnce(int32_t &result, uint8_t oversamplingRate);
+ int16_t startMeasurePressureOnce(void);
+ int16_t startMeasurePressureOnce(uint8_t oversamplingRate);
+ int16_t getSingleResult(int32_t &result);
+
+ //Background Mode
+ int16_t startMeasureTempCont(uint8_t measureRate, uint8_t oversamplingRate);
+ int16_t startMeasurePressureCont(uint8_t measureRate, uint8_t oversamplingRate);
+ int16_t startMeasureBothCont(uint8_t tempMr, uint8_t tempOsr, uint8_t prsMr, uint8_t prsOsr);
+ int16_t getContResults(int32_t *tempBuffer, uint8_t &tempCount, int32_t *prsBuffer, uint8_t &prsCount);
+
+ //Interrupt Control
+ int16_t setInterruptPolarity(uint8_t polarity);
+ int16_t setInterruptSources(uint8_t fifoFull, uint8_t tempReady, uint8_t prsReady);
+ int16_t getIntStatusFifoFull(void);
+ int16_t getIntStatusTempReady(void);
+ int16_t getIntStatusPrsReady(void);
+
+ //function to fix a hardware problem on some devices
+ int16_t correctTemp(void);
+
+private:
+ //scaling factor table
+ static const int32_t scaling_facts[HP303B__NUM_OF_SCAL_FACTS];
+
+ //enum for operating mode
+ enum Mode
+ {
+ IDLE = 0x00,
+ CMD_PRS = 0x01,
+ CMD_TEMP = 0x02,
+ INVAL_OP_CMD_BOTH = 0x03, //invalid
+ INVAL_OP_CONT_NONE = 0x04, //invalid
+ CONT_PRS = 0x05,
+ CONT_TMP = 0x06,
+ CONT_BOTH = 0x07
+ };
+ Mode m_opMode;
+
+ //flags
+ uint8_t m_initFail;
+ uint8_t m_productID;
+ uint8_t m_revisionID;
+
+ //settings
+ uint8_t m_tempMr;
+ uint8_t m_tempOsr;
+ uint8_t m_prsMr;
+ uint8_t m_prsOsr;
+ uint8_t m_tempSensor;
+
+ //compensation coefficients
+ int32_t m_c0Half;
+ int32_t m_c1;
+ int32_t m_c00;
+ int32_t m_c10;
+ int32_t m_c01;
+ int32_t m_c11;
+ int32_t m_c20;
+ int32_t m_c21;
+ int32_t m_c30;
+ //last measured scaled temperature
+ //(necessary for pressure compensation)
+ double m_lastTempScal;
+
+ //bus specific
+ uint8_t m_SpiI2c; //0=SPI, 1=I2C
+ //used for I2C
+ TwoWire *m_i2cbus;
+ uint8_t m_slaveAddress;
+ //used for SPI
+ SPIClass *m_spibus;
+ int32_t m_chipSelect;
+ uint8_t m_threeWire;
+
+ //measurement
+ void init(void);
+ int16_t readcoeffs(void);
+ int16_t setOpMode(uint8_t background, uint8_t temperature, uint8_t pressure);
+ int16_t setOpMode(uint8_t opMode);
+ int16_t configTemp(uint8_t temp_mr, uint8_t temp_osr);
+ int16_t configPressure(uint8_t prs_mr, uint8_t prs_osr);
+ uint16_t calcBusyTime(uint16_t temp_rate, uint16_t temp_osr);
+ int16_t getTemp(int32_t *result);
+ int16_t getPressure(int32_t *result);
+ int16_t getFIFOvalue(int32_t *value);
+ int32_t calcTemp(int32_t raw);
+ int32_t calcPressure(int32_t raw);
+
+ //bus specific
+ int16_t readByte(uint8_t regAddress);
+ int16_t readByteSPI(uint8_t regAddress);
+ int16_t readBlock(uint8_t regAddress, uint8_t length, uint8_t *buffer);
+ int16_t readBlockSPI(uint8_t regAddress, uint8_t length, uint8_t *readbuffer);
+ int16_t writeByte(uint8_t regAddress, uint8_t data);
+ int16_t writeByte(uint8_t regAddress, uint8_t data, uint8_t check);
+ int16_t writeByteSpi(uint8_t regAddress, uint8_t data, uint8_t check);
+ int16_t writeByteBitfield(uint8_t data, uint8_t regAddress, uint8_t mask, uint8_t shift);
+ int16_t writeByteBitfield(uint8_t data, uint8_t regAddress, uint8_t mask, uint8_t shift, uint8_t check);
+ int16_t readByteBitfield(uint8_t regAddress, uint8_t mask, uint8_t shift);
+};
+
+#endif
diff --git a/lib/LOLIN_HP303B/src/util/hp303b_consts.h b/lib/LOLIN_HP303B/src/util/hp303b_consts.h
new file mode 100644
index 000000000..f93629e93
--- /dev/null
+++ b/lib/LOLIN_HP303B/src/util/hp303b_consts.h
@@ -0,0 +1,258 @@
+/**
+ *
+ *
+ */
+
+#ifndef __HP303B_CONSTS_H_
+#define __HP303B_CONSTS_H_
+
+
+ //general Constants
+#define HP303B__PROD_ID 0U
+#define HP303B__STD_SLAVE_ADDRESS 0x77U
+#define HP303B__SPI_WRITE_CMD 0x00U
+#define HP303B__SPI_READ_CMD 0x80U
+#define HP303B__SPI_RW_MASK 0x80U
+#define HP303B__SPI_MAX_FREQ 100000U
+
+#define HP303B__LSB 0x01U
+
+#define HP303B__TEMP_STD_MR 2U
+#define HP303B__TEMP_STD_OSR 3U
+#define HP303B__PRS_STD_MR 2U
+#define HP303B__PRS_STD_OSR 3U
+#define HP303B__OSR_SE 3U
+//we use 0.1 mS units for time calculations, so 10 units are one millisecond
+#define HP303B__BUSYTIME_SCALING 10U
+// DPS310 has 10 milliseconds of spare time for each synchronous measurement / per second for asynchronous measurements
+// this is for error prevention on friday-afternoon-products :D
+// you can set it to 0 if you dare, but there is no warranty that it will still work
+#define HP303B__BUSYTIME_FAILSAFE 10U
+#define HP303B__MAX_BUSYTIME ((1000U-HP303B__BUSYTIME_FAILSAFE)*HP303B__BUSYTIME_SCALING)
+#define HP303B__NUM_OF_SCAL_FACTS 8
+
+#define HP303B__SUCCEEDED 0
+#define HP303B__FAIL_UNKNOWN -1
+#define HP303B__FAIL_INIT_FAILED -2
+#define HP303B__FAIL_TOOBUSY -3
+#define HP303B__FAIL_UNFINISHED -4
+
+
+ //Constants for register manipulation
+ //SPI mode (3 or 4 wire)
+#define HP303B__REG_ADR_SPI3W 0x09U
+#define HP303B__REG_CONTENT_SPI3W 0x01U
+
+
+ //product id
+#define HP303B__REG_INFO_PROD_ID HP303B__REG_ADR_PROD_ID, \
+ HP303B__REG_MASK_PROD_ID, \
+ HP303B__REG_SHIFT_PROD_ID
+#define HP303B__REG_ADR_PROD_ID 0x0DU
+#define HP303B__REG_MASK_PROD_ID 0x0FU
+#define HP303B__REG_SHIFT_PROD_ID 0U
+
+ //revision id
+#define HP303B__REG_INFO_REV_ID HP303B__REG_ADR_REV_ID, \
+ HP303B__REG_MASK_REV_ID, \
+ HP303B__REG_SHIFT_REV_ID
+#define HP303B__REG_ADR_REV_ID 0x0DU
+#define HP303B__REG_MASK_REV_ID 0xF0U
+#define HP303B__REG_SHIFT_REV_ID 4U
+
+ //operating mode
+#define HP303B__REG_INFO_OPMODE HP303B__REG_ADR_OPMODE, \
+ HP303B__REG_MASK_OPMODE, \
+ HP303B__REG_SHIFT_OPMODE
+#define HP303B__REG_ADR_OPMODE 0x08U
+#define HP303B__REG_MASK_OPMODE 0x07U
+#define HP303B__REG_SHIFT_OPMODE 0U
+
+
+ //temperature measure rate
+#define HP303B__REG_INFO_TEMP_MR HP303B__REG_ADR_TEMP_MR, \
+ HP303B__REG_MASK_TEMP_MR, \
+ HP303B__REG_SHIFT_TEMP_MR
+#define HP303B__REG_ADR_TEMP_MR 0x07U
+#define HP303B__REG_MASK_TEMP_MR 0x70U
+#define HP303B__REG_SHIFT_TEMP_MR 4U
+
+ //temperature oversampling rate
+#define HP303B__REG_INFO_TEMP_OSR HP303B__REG_ADR_TEMP_OSR, \
+ HP303B__REG_MASK_TEMP_OSR, \
+ HP303B__REG_SHIFT_TEMP_OSR
+#define HP303B__REG_ADR_TEMP_OSR 0x07U
+#define HP303B__REG_MASK_TEMP_OSR 0x07U
+#define HP303B__REG_SHIFT_TEMP_OSR 0U
+
+ //temperature sensor
+#define HP303B__REG_INFO_TEMP_SENSOR HP303B__REG_ADR_TEMP_SENSOR, \
+ HP303B__REG_MASK_TEMP_SENSOR, \
+ HP303B__REG_SHIFT_TEMP_SENSOR
+#define HP303B__REG_ADR_TEMP_SENSOR 0x07U
+#define HP303B__REG_MASK_TEMP_SENSOR 0x80U
+#define HP303B__REG_SHIFT_TEMP_SENSOR 7U
+
+ //temperature sensor recommendation
+#define HP303B__REG_INFO_TEMP_SENSORREC HP303B__REG_ADR_TEMP_SENSORREC, \
+ HP303B__REG_MASK_TEMP_SENSORREC, \
+ HP303B__REG_SHIFT_TEMP_SENSORREC
+#define HP303B__REG_ADR_TEMP_SENSORREC 0x28U
+#define HP303B__REG_MASK_TEMP_SENSORREC 0x80U
+#define HP303B__REG_SHIFT_TEMP_SENSORREC 7U
+
+ //temperature shift enable (if temp_osr>3)
+#define HP303B__REG_INFO_TEMP_SE HP303B__REG_ADR_TEMP_SE, \
+ HP303B__REG_MASK_TEMP_SE, \
+ HP303B__REG_SHIFT_TEMP_SE
+#define HP303B__REG_ADR_TEMP_SE 0x09U
+#define HP303B__REG_MASK_TEMP_SE 0x08U
+#define HP303B__REG_SHIFT_TEMP_SE 3U
+
+
+ //pressure measure rate
+#define HP303B__REG_INFO_PRS_MR HP303B__REG_ADR_PRS_MR, \
+ HP303B__REG_MASK_PRS_MR, \
+ HP303B__REG_SHIFT_PRS_MR
+#define HP303B__REG_ADR_PRS_MR 0x06U
+#define HP303B__REG_MASK_PRS_MR 0x70U
+#define HP303B__REG_SHIFT_PRS_MR 4U
+
+ //pressure oversampling rate
+#define HP303B__REG_INFO_PRS_OSR HP303B__REG_ADR_PRS_OSR, \
+ HP303B__REG_MASK_PRS_OSR, \
+ HP303B__REG_SHIFT_PRS_OSR
+#define HP303B__REG_ADR_PRS_OSR 0x06U
+#define HP303B__REG_MASK_PRS_OSR 0x07U
+#define HP303B__REG_SHIFT_PRS_OSR 0U
+
+ //pressure shift enable (if prs_osr>3)
+#define HP303B__REG_INFO_PRS_SE HP303B__REG_ADR_PRS_SE, \
+ HP303B__REG_MASK_PRS_SE, \
+ HP303B__REG_SHIFT_PRS_SE
+#define HP303B__REG_ADR_PRS_SE 0x09U
+#define HP303B__REG_MASK_PRS_SE 0x04U
+#define HP303B__REG_SHIFT_PRS_SE 2U
+
+
+ //temperature ready flag
+#define HP303B__REG_INFO_TEMP_RDY HP303B__REG_ADR_TEMP_RDY, \
+ HP303B__REG_MASK_TEMP_RDY, \
+ HP303B__REG_SHIFT_TEMP_RDY
+#define HP303B__REG_ADR_TEMP_RDY 0x08U
+#define HP303B__REG_MASK_TEMP_RDY 0x20U
+#define HP303B__REG_SHIFT_TEMP_RDY 5U
+
+ //pressure ready flag
+#define HP303B__REG_INFO_PRS_RDY HP303B__REG_ADR_PRS_RDY, \
+ HP303B__REG_MASK_PRS_RDY, \
+ HP303B__REG_SHIFT_PRS_RDY
+#define HP303B__REG_ADR_PRS_RDY 0x08U
+#define HP303B__REG_MASK_PRS_RDY 0x10U
+#define HP303B__REG_SHIFT_PRS_RDY 4U
+
+ //pressure value
+#define HP303B__REG_ADR_PRS 0x00U
+#define HP303B__REG_LEN_PRS 3U
+
+ //temperature value
+#define HP303B__REG_ADR_TEMP 0x03U
+#define HP303B__REG_LEN_TEMP 3U
+
+ //compensation coefficients
+#define HP303B__REG_ADR_COEF 0x10U
+#define HP303B__REG_LEN_COEF 18
+
+
+ //FIFO enable
+#define HP303B__REG_INFO_FIFO_EN HP303B__REG_ADR_FIFO_EN, \
+ HP303B__REG_MASK_FIFO_EN, \
+ HP303B__REG_SHIFT_FIFO_EN
+#define HP303B__REG_ADR_FIFO_EN 0x09U
+#define HP303B__REG_MASK_FIFO_EN 0x02U
+#define HP303B__REG_SHIFT_FIFO_EN 1U
+
+ //FIFO flush
+#define HP303B__REG_INFO_FIFO_FL HP303B__REG_ADR_FIFO_EN, \
+ HP303B__REG_MASK_FIFO_EN, \
+ HP303B__REG_SHIFT_FIFO_EN
+#define HP303B__REG_ADR_FIFO_FL 0x0CU
+#define HP303B__REG_MASK_FIFO_FL 0x80U
+#define HP303B__REG_SHIFT_FIFO_FL 7U
+
+ //FIFO empty
+#define HP303B__REG_INFO_FIFO_EMPTY HP303B__REG_ADR_FIFO_EMPTY, \
+ HP303B__REG_MASK_FIFO_EMPTY, \
+ HP303B__REG_SHIFT_FIFO_EMPTY
+#define HP303B__REG_ADR_FIFO_EMPTY 0x0BU
+#define HP303B__REG_MASK_FIFO_EMPTY 0x01U
+#define HP303B__REG_SHIFT_FIFO_EMPTY 0U
+
+ //FIFO full
+#define HP303B__REG_INFO_FIFO_FULL HP303B__REG_ADR_FIFO_FULL, \
+ HP303B__REG_MASK_FIFO_FULL, \
+ HP303B__REG_SHIFT_FIFO_FULL
+#define HP303B__REG_ADR_FIFO_FULL 0x0BU
+#define HP303B__REG_MASK_FIFO_FULL 0x02U
+#define HP303B__REG_SHIFT_FIFO_FULL 1U
+
+
+ //INT HL
+#define HP303B__REG_INFO_INT_HL HP303B__REG_ADR_INT_HL, \
+ HP303B__REG_MASK_INT_HL, \
+ HP303B__REG_SHIFT_INT_HL
+#define HP303B__REG_ADR_INT_HL 0x09U
+#define HP303B__REG_MASK_INT_HL 0x80U
+#define HP303B__REG_SHIFT_INT_HL 7U
+
+ //INT FIFO enable
+#define HP303B__REG_INFO_INT_EN_FIFO HP303B__REG_ADR_INT_EN_FIFO, \
+ HP303B__REG_MASK_INT_EN_FIFO, \
+ HP303B__REG_SHIFT_INT_EN_FIFO
+#define HP303B__REG_ADR_INT_EN_FIFO 0x09U
+#define HP303B__REG_MASK_INT_EN_FIFO 0x40U
+#define HP303B__REG_SHIFT_INT_EN_FIFO 6U
+
+ //INT TEMP enable
+#define HP303B__REG_INFO_INT_EN_TEMP HP303B__REG_ADR_INT_EN_TEMP, \
+ HP303B__REG_MASK_INT_EN_TEMP, \
+ HP303B__REG_SHIFT_INT_EN_TEMP
+#define HP303B__REG_ADR_INT_EN_TEMP 0x09U
+#define HP303B__REG_MASK_INT_EN_TEMP 0x20U
+#define HP303B__REG_SHIFT_INT_EN_TEMP 5U
+
+ //INT PRS enable
+#define HP303B__REG_INFO_INT_EN_PRS HP303B__REG_ADR_INT_EN_PRS, \
+ HP303B__REG_MASK_INT_EN_PRS, \
+ HP303B__REG_SHIFT_INT_EN_PRS
+#define HP303B__REG_ADR_INT_EN_PRS 0x09U
+#define HP303B__REG_MASK_INT_EN_PRS 0x10U
+#define HP303B__REG_SHIFT_INT_EN_PRS 4U
+
+ //INT FIFO flag
+#define HP303B__REG_INFO_INT_FLAG_FIFO HP303B__REG_ADR_INT_FLAG_FIFO, \
+ HP303B__REG_MASK_INT_FLAG_FIFO, \
+ HP303B__REG_SHIFT_INT_FLAG_FIFO
+#define HP303B__REG_ADR_INT_FLAG_FIFO 0x0AU
+#define HP303B__REG_MASK_INT_FLAG_FIFO 0x04U
+#define HP303B__REG_SHIFT_INT_FLAG_FIFO 2U
+
+ //INT TMP flag
+#define HP303B__REG_INFO_INT_FLAG_TEMP HP303B__REG_ADR_INT_FLAG_TEMP, \
+ HP303B__REG_MASK_INT_FLAG_TEMP, \
+ HP303B__REG_SHIFT_INT_FLAG_TEMP
+#define HP303B__REG_ADR_INT_FLAG_TEMP 0x0AU
+#define HP303B__REG_MASK_INT_FLAG_TEMP 0x02U
+#define HP303B__REG_SHIFT_INT_FLAG_TEMP 1U
+
+ //INT PRS flag
+#define HP303B__REG_INFO_INT_FLAG_PRS HP303B__REG_ADR_INT_FLAG_PRS, \
+ HP303B__REG_MASK_INT_FLAG_PRS, \
+ HP303B__REG_SHIFT_INT_FLAG_PRS
+#define HP303B__REG_ADR_INT_FLAG_PRS 0x0AU
+#define HP303B__REG_MASK_INT_FLAG_PRS 0x01U
+#define HP303B__REG_SHIFT_INT_FLAG_PRS 0U
+
+
+
+#endif /* DPS310_CONSTS_H_ */
diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino
index 5622bcde9..25a8a2203 100644
--- a/tasmota/support_features.ino
+++ b/tasmota/support_features.ino
@@ -572,8 +572,10 @@ void GetFeatures(void)
#ifdef USE_MCP9808
feature6 |= 0x00002000; // xsns_72_mcp9808.ino
#endif
+#ifdef USE_HP303B
+ feature6 |= 0x00004000; // xsns_73_hp303b.ino
+#endif
-// feature6 |= 0x00004000;
// feature6 |= 0x00008000;
// feature6 |= 0x00010000;
diff --git a/tasmota/xsns_73_hp303b.ino b/tasmota/xsns_73_hp303b.ino
new file mode 100644
index 000000000..e5df20508
--- /dev/null
+++ b/tasmota/xsns_73_hp303b.ino
@@ -0,0 +1,161 @@
+/*
+ xsns_72_hp303b.ino - HP303B digital barometric air pressure sensor support for Tasmota
+
+ Copyright (C) 2020 Theo Arends
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+#ifdef USE_I2C
+#ifdef USE_HP303B
+/*********************************************************************************************\
+ * HP303B - Gas (TVOC - Total Volatile Organic Compounds) and Air Quality (CO2)
+ *
+ * Source: Lolin LOLIN_HP303B_Library
+ *
+ * I2C Address: 0x77 or 0x76
+\*********************************************************************************************/
+
+#define XSNS_73 73
+#define XI2C_52 52 // See I2CDEVICES.md
+
+#define HP303B_ADDR1 0x77
+#define HP303B_ADDR2 0x76
+
+#include
+// HP303B Opject
+LOLIN_HP303B HP303BSensor = LOLIN_HP303B();
+
+struct HP303BDATA
+{
+ uint8_t address;
+ uint8_t addresses[2] = {HP303B_ADDR1, HP303B_ADDR2};
+ uint8_t type = 0;
+ uint8_t valid = 0;
+ int32_t temperature;
+ int32_t pressure;
+ int16_t oversampling = 7;
+ char types[7] = "HP303B";
+} HP303B;
+
+/*********************************************************************************************/
+
+bool HP303B_Read(int32_t &t, int32_t &p, uint8_t hp303b_address)
+{
+ HP303BSensor.begin(hp303b_address);
+
+ int16_t ret;
+
+ ret = HP303BSensor.measureTempOnce(t, HP303B.oversampling);
+ if (ret != 0)
+ return false;
+
+ ret = HP303BSensor.measurePressureOnce(p, HP303B.oversampling);
+ if (ret != 0)
+ return false;
+
+ HP303B.temperature = ConvertTemp(t);
+ HP303B.pressure = ConvertPressure(p);
+
+ return true;
+}
+
+/********************************************************************************************/
+
+void HP303B_Detect(void)
+{
+ for (uint32_t i = 0; i < sizeof(HP303B.addresses); i++)
+ {
+ if (!I2cSetDevice(HP303B.addresses[i]))
+ {
+ continue;
+ }
+
+ int32_t t;
+ int32_t p;
+ if (HP303B_Read(t, p, HP303B.addresses[i]))
+ {
+ I2cSetActiveFound(HP303B.addresses[i], HP303B.types);
+ HP303B.address = HP303B.addresses[i];
+ HP303B.type = 1;
+ }
+ }
+}
+
+void HP303B_Show(bool json)
+{
+
+ if (HP303B_Read(HP303B.temperature, HP303B.pressure, HP303B.address))
+ {
+ if (json)
+ {
+ ResponseAppend_P(PSTR(",\"HP303B\":{\"" D_JSON_TEMPERATURE "\":%d,\"" D_JSON_PRESSURE "\":%d"), HP303B.temperature, HP303B.pressure);
+#ifdef USE_DOMOTICZ
+ if (0 == tele_period)
+ {
+ DomoticzSensor(DZ_TEMP, HP303B.temperature);
+ }
+#endif // USE_DOMOTICZ
+#ifdef USE_WEBSERVER
+ }
+ else
+ {
+ char str_temperature[12];
+ char str_pressure[12];
+
+ itoa(HP303B.temperature, str_temperature, 10);
+ itoa(HP303B.pressure, str_pressure, 10);
+
+ WSContentSend_PD(HTTP_SNS_TEMP, "HP303B", str_temperature, TempUnit());
+ WSContentSend_PD(HTTP_SNS_PRESSURE, "HP303B", str_pressure, PressureUnit().c_str());
+#endif // USE_WEBSERVER
+ }
+ }
+}
+
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+
+bool Xsns73(uint8_t function)
+{
+ if (!I2cEnabled(XI2C_52))
+ {
+ return false;
+ }
+
+ bool result = false;
+
+ if (FUNC_INIT == function)
+ {
+ HP303B_Detect();
+ }
+ else if (HP303B.type)
+ {
+ switch (function)
+ {
+ case FUNC_JSON_APPEND:
+ HP303B_Show(1);
+ break;
+#ifdef USE_WEBSERVER
+ case FUNC_WEB_SENSOR:
+ HP303B_Show(0);
+ break;
+#endif // USE_WEBSERVER
+ }
+ }
+ return result;
+}
+
+#endif // USE_HP303B
+#endif // USE_I2C
diff --git a/tools/decode-status.py b/tools/decode-status.py
index 67898d784..b77ddf2c6 100755
--- a/tools/decode-status.py
+++ b/tools/decode-status.py
@@ -204,7 +204,7 @@ a_features = [[
"USE_KEELOQ","USE_HRXL","USE_SONOFF_D1","USE_HDC1080",
"USE_IAQ","USE_DISPLAY_SEVENSEG","USE_AS3935","USE_PING",
"USE_WINDMETER","USE_OPENTHERM","USE_THERMOSTAT","USE_VEML6075",
- "USE_VEML7700","USE_MCP9808","","",
+ "USE_VEML7700","USE_MCP9808","USE_HP303B","",
"","","","",
"","","","",
"","","","",
From 2da09526ba41f73663b7ddda55df80c3104c2407 Mon Sep 17 00:00:00 2001
From: Robert Jaakke
Date: Sat, 6 Jun 2020 23:05:55 +0200
Subject: [PATCH 02/35] switched to float values and converted Pa to hPa
---
tasmota/xsns_73_hp303b.ino | 64 ++++++++++++++++++--------------------
1 file changed, 31 insertions(+), 33 deletions(-)
diff --git a/tasmota/xsns_73_hp303b.ino b/tasmota/xsns_73_hp303b.ino
index e5df20508..3f508b7d8 100644
--- a/tasmota/xsns_73_hp303b.ino
+++ b/tasmota/xsns_73_hp303b.ino
@@ -36,36 +36,35 @@
// HP303B Opject
LOLIN_HP303B HP303BSensor = LOLIN_HP303B();
-struct HP303BDATA
-{
- uint8_t address;
- uint8_t addresses[2] = {HP303B_ADDR1, HP303B_ADDR2};
- uint8_t type = 0;
- uint8_t valid = 0;
- int32_t temperature;
- int32_t pressure;
- int16_t oversampling = 7;
- char types[7] = "HP303B";
-} HP303B;
+uint8_t address;
+uint8_t addresses[2] = {HP303B_ADDR1, HP303B_ADDR2};
+uint8_t type = 0;
+uint8_t valid = 0;
+float temperature;
+float pressure;
+int16_t oversampling = 7;
+char types[7] = "HP303B";
/*********************************************************************************************/
-bool HP303B_Read(int32_t &t, int32_t &p, uint8_t hp303b_address)
+bool HP303B_Read(float &temperature, float &pressure, uint8_t hp303b_address)
{
HP303BSensor.begin(hp303b_address);
+ int32_t t;
+ int32_t p;
int16_t ret;
- ret = HP303BSensor.measureTempOnce(t, HP303B.oversampling);
+ ret = HP303BSensor.measureTempOnce(t, oversampling);
if (ret != 0)
return false;
- ret = HP303BSensor.measurePressureOnce(p, HP303B.oversampling);
+ ret = HP303BSensor.measurePressureOnce(p, oversampling);
if (ret != 0)
return false;
- HP303B.temperature = ConvertTemp(t);
- HP303B.pressure = ConvertPressure(p);
+ temperature = (float)ConvertTemp(t);
+ pressure = (float)ConvertPressure(p) / 100; //conversion to hPa
return true;
}
@@ -74,20 +73,20 @@ bool HP303B_Read(int32_t &t, int32_t &p, uint8_t hp303b_address)
void HP303B_Detect(void)
{
- for (uint32_t i = 0; i < sizeof(HP303B.addresses); i++)
+ for (uint32_t i = 0; i < sizeof(addresses); i++)
{
- if (!I2cSetDevice(HP303B.addresses[i]))
+ if (!I2cSetDevice(addresses[i]))
{
continue;
}
- int32_t t;
- int32_t p;
- if (HP303B_Read(t, p, HP303B.addresses[i]))
+ float t;
+ float p;
+ if (HP303B_Read(t, p, addresses[i]))
{
- I2cSetActiveFound(HP303B.addresses[i], HP303B.types);
- HP303B.address = HP303B.addresses[i];
- HP303B.type = 1;
+ I2cSetActiveFound(addresses[i], types);
+ address = addresses[i];
+ type = 1;
}
}
}
@@ -95,26 +94,25 @@ void HP303B_Detect(void)
void HP303B_Show(bool json)
{
- if (HP303B_Read(HP303B.temperature, HP303B.pressure, HP303B.address))
+ if (HP303B_Read(temperature, pressure, address))
{
if (json)
{
- ResponseAppend_P(PSTR(",\"HP303B\":{\"" D_JSON_TEMPERATURE "\":%d,\"" D_JSON_PRESSURE "\":%d"), HP303B.temperature, HP303B.pressure);
+ ResponseAppend_P(PSTR(",\"HP303B\":{\"" D_JSON_TEMPERATURE "\":%d,\"" D_JSON_PRESSURE "\":%d"), temperature, pressure);
#ifdef USE_DOMOTICZ
if (0 == tele_period)
{
- DomoticzSensor(DZ_TEMP, HP303B.temperature);
+ DomoticzSensor(DZ_TEMP, temperature);
}
#endif // USE_DOMOTICZ
#ifdef USE_WEBSERVER
}
else
{
- char str_temperature[12];
- char str_pressure[12];
-
- itoa(HP303B.temperature, str_temperature, 10);
- itoa(HP303B.pressure, str_pressure, 10);
+ char str_temperature[33];
+ dtostrfd(temperature, Settings.flag2.temperature_resolution, str_temperature);
+ char str_pressure[33];
+ dtostrfd(pressure, Settings.flag2.pressure_resolution, str_pressure);
WSContentSend_PD(HTTP_SNS_TEMP, "HP303B", str_temperature, TempUnit());
WSContentSend_PD(HTTP_SNS_PRESSURE, "HP303B", str_pressure, PressureUnit().c_str());
@@ -140,7 +138,7 @@ bool Xsns73(uint8_t function)
{
HP303B_Detect();
}
- else if (HP303B.type)
+ else if (type)
{
switch (function)
{
From d3a59fd65c3f5834d9fb61eee1374d50a2f14db3 Mon Sep 17 00:00:00 2001
From: Robert Jaakke
Date: Sun, 7 Jun 2020 16:33:28 +0200
Subject: [PATCH 03/35] Changed driver to measure float
---
lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp | 82 +++++++++++++--------------
lib/LOLIN_HP303B/src/LOLIN_HP303B.h | 22 +++----
tasmota/xsns_73_hp303b.ino | 17 +++---
3 files changed, 60 insertions(+), 61 deletions(-)
diff --git a/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp b/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp
index 43fcd435e..fdeebd6a0 100644
--- a/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp
+++ b/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp
@@ -181,7 +181,7 @@ int16_t LOLIN_HP303B::standby(void)
* -2 if the object initialization failed
* -1 on other fail
*/
-int16_t LOLIN_HP303B::measureTempOnce(int32_t &result)
+int16_t LOLIN_HP303B::measureTempOnce(float &result)
{
return measureTempOnce(result, m_tempOsr);
}
@@ -202,7 +202,7 @@ int16_t LOLIN_HP303B::measureTempOnce(int32_t &result)
* -2 if the object initialization failed
* -1 on other fail
*/
-int16_t LOLIN_HP303B::measureTempOnce(int32_t &result, uint8_t oversamplingRate)
+int16_t LOLIN_HP303B::measureTempOnce(float &result, uint8_t oversamplingRate)
{
//Start measurement
int16_t ret = startMeasureTempOnce(oversamplingRate);
@@ -286,7 +286,7 @@ int16_t LOLIN_HP303B::startMeasureTempOnce(uint8_t oversamplingRate)
* -2 if the object initialization failed
* -1 on other fail
*/
-int16_t LOLIN_HP303B::measurePressureOnce(int32_t &result)
+int16_t LOLIN_HP303B::measurePressureOnce(float &result)
{
return measurePressureOnce(result, m_prsOsr);
}
@@ -307,7 +307,7 @@ int16_t LOLIN_HP303B::measurePressureOnce(int32_t &result)
* -2 if the object initialization failed
* -1 on other fail
*/
-int16_t LOLIN_HP303B::measurePressureOnce(int32_t &result, uint8_t oversamplingRate)
+int16_t LOLIN_HP303B::measurePressureOnce(float &result, uint8_t oversamplingRate)
{
//start the measurement
int16_t ret = startMeasurePressureOnce(oversamplingRate);
@@ -388,7 +388,7 @@ int16_t LOLIN_HP303B::startMeasurePressureOnce(uint8_t oversamplingRate)
* -2 if the object initialization failed
* -1 on other fail
*/
-int16_t LOLIN_HP303B::getSingleResult(int32_t &result)
+int16_t LOLIN_HP303B::getSingleResult(float &result)
{
//abort if initialization failed
if(m_initFail)
@@ -636,9 +636,9 @@ int16_t LOLIN_HP303B::startMeasureBothCont(uint8_t tempMr,
* -2 if the object initialization failed
* -1 on other fail
*/
-int16_t LOLIN_HP303B::getContResults(int32_t *tempBuffer,
+int16_t LOLIN_HP303B::getContResults(float *tempBuffer,
uint8_t &tempCount,
- int32_t *prsBuffer,
+ float *prsBuffer,
uint8_t &prsCount)
{
if(m_initFail)
@@ -660,7 +660,7 @@ int16_t LOLIN_HP303B::getContResults(int32_t *tempBuffer,
//while FIFO is not empty
while(readByteBitfield(HP303B__REG_INFO_FIFO_EMPTY) == 0)
{
- int32_t result;
+ float result;
//read next result from FIFO
int16_t type = getFIFOvalue(&result);
switch(type)
@@ -820,13 +820,13 @@ int16_t LOLIN_HP303B::correctTemp(void)
writeByte(0x62, 0x02);
writeByte(0x0E, 0x00);
writeByte(0x0F, 0x00);
-
+
//perform a first temperature measurement (again)
//the most recent temperature will be saved internally
//and used for compensation when calculating pressure
- int32_t trash;
+ float trash;
measureTempOnce(trash);
-
+
return HP303B__SUCCEEDED;
}
@@ -892,13 +892,13 @@ void LOLIN_HP303B::init(void)
//perform a first temperature measurement
//the most recent temperature will be saved internally
//and used for compensation when calculating pressure
- int32_t trash;
+ float trash;
measureTempOnce(trash);
//make sure the HP303B is in standby after initialization
- standby();
+ standby();
- // Fix IC with a fuse bit problem, which lead to a wrong temperature
+ // Fix IC with a fuse bit problem, which lead to a wrong temperature
// Should not affect ICs without this problem
correctTemp();
}
@@ -1192,9 +1192,9 @@ uint16_t LOLIN_HP303B::calcBusyTime(uint16_t mr, uint16_t osr)
* returns: 0 on success
* -1 on fail;
*/
-int16_t LOLIN_HP303B::getTemp(int32_t *result)
+int16_t LOLIN_HP303B::getTemp(float *result)
{
- uint8_t buffer[3] = {0};
+ unsigned char buffer[3] = {0};
//read raw pressure data to buffer
int16_t i = readBlock(HP303B__REG_ADR_TEMP,
@@ -1207,14 +1207,14 @@ int16_t LOLIN_HP303B::getTemp(int32_t *result)
}
//compose raw temperature value from buffer
- int32_t temp = (uint32_t)buffer[0] << 16
- | (uint32_t)buffer[1] << 8
- | (uint32_t)buffer[2];
+ float temp = buffer[0] << 16
+ | buffer[1] << 8
+ | buffer[2];
//recognize non-32-bit negative numbers
//and convert them to 32-bit negative numbers using 2's complement
- if(temp & ((uint32_t)1 << 23))
+ if(temp > 0x7FFFFF)
{
- temp -= (uint32_t)1 << 24;
+ temp = temp - 0x1000000;
}
//return temperature
@@ -1229,9 +1229,9 @@ int16_t LOLIN_HP303B::getTemp(int32_t *result)
* returns: 0 on success
* -1 on fail;
*/
-int16_t LOLIN_HP303B::getPressure(int32_t *result)
+int16_t LOLIN_HP303B::getPressure(float *result)
{
- uint8_t buffer[3] = {0};
+ unsigned char buffer[3] = {0};
//read raw pressure data to buffer
int16_t i = readBlock(HP303B__REG_ADR_PRS,
HP303B__REG_LEN_PRS,
@@ -1244,14 +1244,12 @@ int16_t LOLIN_HP303B::getPressure(int32_t *result)
}
//compose raw pressure value from buffer
- int32_t prs = (uint32_t)buffer[0] << 16
- | (uint32_t)buffer[1] << 8
- | (uint32_t)buffer[2];
+ float prs = buffer[0] << 16 | buffer[1] << 8 | buffer[2];
//recognize non-32-bit negative numbers
//and convert them to 32-bit negative numbers using 2's complement
- if(prs & ((uint32_t)1 << 23))
+ if(prs > 0x7FFFFF)
{
- prs -= (uint32_t)1 << 24;
+ prs = prs - 0x1000000;
}
*result = calcPressure(prs);
@@ -1266,7 +1264,7 @@ int16_t LOLIN_HP303B::getPressure(int32_t *result)
* 0 if result is a temperature raw value
* 1 if result is a pressure raw value
*/
-int16_t LOLIN_HP303B::getFIFOvalue(int32_t* value)
+int16_t LOLIN_HP303B::getFIFOvalue(float *value)
{
//abort on invalid argument
if(value == NULL)
@@ -1274,7 +1272,7 @@ int16_t LOLIN_HP303B::getFIFOvalue(int32_t* value)
return HP303B__FAIL_UNKNOWN;
}
- uint8_t buffer[HP303B__REG_LEN_PRS] = {0};
+ unsigned char buffer[HP303B__REG_LEN_PRS] = {0};
//always read from pressure raw value register
int16_t i = readBlock(HP303B__REG_ADR_PRS,
HP303B__REG_LEN_PRS,
@@ -1286,14 +1284,14 @@ int16_t LOLIN_HP303B::getFIFOvalue(int32_t* value)
return HP303B__FAIL_UNKNOWN;
}
//compose raw pressure value from buffer
- *value = (uint32_t)buffer[0] << 16
- | (uint32_t)buffer[1] << 8
- | (uint32_t)buffer[2];
+ *value = buffer[0] << 16
+ | buffer[1] << 8
+ | buffer[2];
//recognize non-32-bit negative numbers
//and convert them to 32-bit negative numbers using 2's complement
- if(*value & ((uint32_t)1 << 23))
+ if(*value > 0x7FFFFF)
{
- *value -= (uint32_t)1 << 24;
+ *value = *value - 0x1000000;
}
//least significant bit shows measurement type
@@ -1305,10 +1303,10 @@ int16_t LOLIN_HP303B::getFIFOvalue(int32_t* value)
* raw: raw temperature value read from HP303B
* returns: temperature value in °C
*/
-int32_t LOLIN_HP303B::calcTemp(int32_t raw)
+float LOLIN_HP303B::calcTemp(float raw)
{
double temp = raw;
-
+
//scale temperature according to scaling table and oversampling
temp /= scaling_facts[m_tempOsr];
@@ -1320,7 +1318,7 @@ int32_t LOLIN_HP303B::calcTemp(int32_t raw)
temp = m_c0Half + m_c1 * temp;
//return temperature
- return (int32_t)temp;
+ return (float)temp;
}
/**
@@ -1328,7 +1326,7 @@ int32_t LOLIN_HP303B::calcTemp(int32_t raw)
* raw: raw pressure value read from HP303B
* returns: pressure value in Pa
*/
-int32_t LOLIN_HP303B::calcPressure(int32_t raw)
+float LOLIN_HP303B::calcPressure(float raw)
{
double prs = raw;
@@ -1341,7 +1339,7 @@ int32_t LOLIN_HP303B::calcPressure(int32_t raw)
+ m_lastTempScal * (m_c01 + prs * (m_c11 + prs * m_c21));
//return pressure
- return (int32_t)prs;
+ return (float)prs;
}
/**
@@ -1357,7 +1355,7 @@ int16_t LOLIN_HP303B::readByte(uint8_t regAddress)
{
return readByteSPI(regAddress);
}
-
+
m_i2cbus->beginTransmission(m_slaveAddress);
m_i2cbus->write(regAddress);
m_i2cbus->endTransmission(0);
@@ -1429,7 +1427,7 @@ int16_t LOLIN_HP303B::readBlock(uint8_t regAddress, uint8_t length, uint8_t *buf
{
return 0; //0 bytes read successfully
}
-
+
m_i2cbus->beginTransmission(m_slaveAddress);
m_i2cbus->write(regAddress);
m_i2cbus->endTransmission(0);
diff --git a/lib/LOLIN_HP303B/src/LOLIN_HP303B.h b/lib/LOLIN_HP303B/src/LOLIN_HP303B.h
index f9c54e356..ce65e17f1 100644
--- a/lib/LOLIN_HP303B/src/LOLIN_HP303B.h
+++ b/lib/LOLIN_HP303B/src/LOLIN_HP303B.h
@@ -34,21 +34,21 @@ public:
int16_t standby(void);
//Command Mode
- int16_t measureTempOnce(int32_t &result);
- int16_t measureTempOnce(int32_t &result, uint8_t oversamplingRate);
+ int16_t measureTempOnce(float &result);
+ int16_t measureTempOnce(float &result, uint8_t oversamplingRate);
int16_t startMeasureTempOnce(void);
int16_t startMeasureTempOnce(uint8_t oversamplingRate);
- int16_t measurePressureOnce(int32_t &result);
- int16_t measurePressureOnce(int32_t &result, uint8_t oversamplingRate);
+ int16_t measurePressureOnce(float &result);
+ int16_t measurePressureOnce(float &result, uint8_t oversamplingRate);
int16_t startMeasurePressureOnce(void);
int16_t startMeasurePressureOnce(uint8_t oversamplingRate);
- int16_t getSingleResult(int32_t &result);
+ int16_t getSingleResult(float &result);
//Background Mode
int16_t startMeasureTempCont(uint8_t measureRate, uint8_t oversamplingRate);
int16_t startMeasurePressureCont(uint8_t measureRate, uint8_t oversamplingRate);
int16_t startMeasureBothCont(uint8_t tempMr, uint8_t tempOsr, uint8_t prsMr, uint8_t prsOsr);
- int16_t getContResults(int32_t *tempBuffer, uint8_t &tempCount, int32_t *prsBuffer, uint8_t &prsCount);
+ int16_t getContResults(float *tempBuffer, uint8_t &tempCount, float *prsBuffer, uint8_t &prsCount);
//Interrupt Control
int16_t setInterruptPolarity(uint8_t polarity);
@@ -122,11 +122,11 @@ private:
int16_t configTemp(uint8_t temp_mr, uint8_t temp_osr);
int16_t configPressure(uint8_t prs_mr, uint8_t prs_osr);
uint16_t calcBusyTime(uint16_t temp_rate, uint16_t temp_osr);
- int16_t getTemp(int32_t *result);
- int16_t getPressure(int32_t *result);
- int16_t getFIFOvalue(int32_t *value);
- int32_t calcTemp(int32_t raw);
- int32_t calcPressure(int32_t raw);
+ int16_t getTemp(float *result);
+ int16_t getPressure(float *result);
+ int16_t getFIFOvalue(float *value);
+ float calcTemp(float raw);
+ float calcPressure(float raw);
//bus specific
int16_t readByte(uint8_t regAddress);
diff --git a/tasmota/xsns_73_hp303b.ino b/tasmota/xsns_73_hp303b.ino
index 3f508b7d8..926a437ad 100644
--- a/tasmota/xsns_73_hp303b.ino
+++ b/tasmota/xsns_73_hp303b.ino
@@ -51,8 +51,8 @@ bool HP303B_Read(float &temperature, float &pressure, uint8_t hp303b_address)
{
HP303BSensor.begin(hp303b_address);
- int32_t t;
- int32_t p;
+ float t;
+ float p;
int16_t ret;
ret = HP303BSensor.measureTempOnce(t, oversampling);
@@ -96,9 +96,15 @@ void HP303B_Show(bool json)
if (HP303B_Read(temperature, pressure, address))
{
+ char str_temperature[33];
+ dtostrfd(temperature, Settings.flag2.temperature_resolution, str_temperature);
+ char str_pressure[33];
+ dtostrfd(pressure, Settings.flag2.pressure_resolution, str_pressure);
+
if (json)
{
- ResponseAppend_P(PSTR(",\"HP303B\":{\"" D_JSON_TEMPERATURE "\":%d,\"" D_JSON_PRESSURE "\":%d"), temperature, pressure);
+ ResponseAppend_P(PSTR(",\"HP303B\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_PRESSURE "\":%s"), str_temperature, str_pressure);
+ ResponseJsonEnd();
#ifdef USE_DOMOTICZ
if (0 == tele_period)
{
@@ -109,11 +115,6 @@ void HP303B_Show(bool json)
}
else
{
- char str_temperature[33];
- dtostrfd(temperature, Settings.flag2.temperature_resolution, str_temperature);
- char str_pressure[33];
- dtostrfd(pressure, Settings.flag2.pressure_resolution, str_pressure);
-
WSContentSend_PD(HTTP_SNS_TEMP, "HP303B", str_temperature, TempUnit());
WSContentSend_PD(HTTP_SNS_PRESSURE, "HP303B", str_pressure, PressureUnit().c_str());
#endif // USE_WEBSERVER
From aa5587f9e19444d33aae379e8f9dcadc1d2cad30 Mon Sep 17 00:00:00 2001
From: Robert Jaakke
Date: Mon, 8 Jun 2020 08:58:38 +0200
Subject: [PATCH 04/35] Mover begin() to detect function
---
tasmota/xsns_73_hp303b.ino | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/tasmota/xsns_73_hp303b.ino b/tasmota/xsns_73_hp303b.ino
index 926a437ad..abff91f09 100644
--- a/tasmota/xsns_73_hp303b.ino
+++ b/tasmota/xsns_73_hp303b.ino
@@ -49,8 +49,6 @@ char types[7] = "HP303B";
bool HP303B_Read(float &temperature, float &pressure, uint8_t hp303b_address)
{
- HP303BSensor.begin(hp303b_address);
-
float t;
float p;
int16_t ret;
@@ -80,6 +78,8 @@ void HP303B_Detect(void)
continue;
}
+ HP303BSensor.begin(addresses[i]);
+
float t;
float p;
if (HP303B_Read(t, p, addresses[i]))
@@ -87,6 +87,7 @@ void HP303B_Detect(void)
I2cSetActiveFound(addresses[i], types);
address = addresses[i];
type = 1;
+ break;
}
}
}
From d6e1ecbe2624695552c101d501ea8ded9193d12e Mon Sep 17 00:00:00 2001
From: Robert Jaakke
Date: Mon, 8 Jun 2020 09:13:01 +0200
Subject: [PATCH 05/35] Moved global variables to struct
---
tasmota/xsns_73_hp303b.ino | 47 +++++++++++++++++++-------------------
1 file changed, 24 insertions(+), 23 deletions(-)
diff --git a/tasmota/xsns_73_hp303b.ino b/tasmota/xsns_73_hp303b.ino
index abff91f09..5a16ca905 100644
--- a/tasmota/xsns_73_hp303b.ino
+++ b/tasmota/xsns_73_hp303b.ino
@@ -35,16 +35,17 @@
#include
// HP303B Opject
LOLIN_HP303B HP303BSensor = LOLIN_HP303B();
+uint8_t bhp303b_addresses[2] = {HP303B_ADDR1, HP303B_ADDR2};
-uint8_t address;
-uint8_t addresses[2] = {HP303B_ADDR1, HP303B_ADDR2};
-uint8_t type = 0;
-uint8_t valid = 0;
-float temperature;
-float pressure;
-int16_t oversampling = 7;
-char types[7] = "HP303B";
-
+struct BHP303B {
+ uint8_t address;
+ uint8_t type = 0;
+ uint8_t valid = 0;
+ float temperature;
+ float pressure;
+ int16_t oversampling = 7;
+ char types[7] = "HP303B";
+} bhp303b_sensor;
/*********************************************************************************************/
bool HP303B_Read(float &temperature, float &pressure, uint8_t hp303b_address)
@@ -53,11 +54,11 @@ bool HP303B_Read(float &temperature, float &pressure, uint8_t hp303b_address)
float p;
int16_t ret;
- ret = HP303BSensor.measureTempOnce(t, oversampling);
+ ret = HP303BSensor.measureTempOnce(t, bhp303b_sensor.oversampling);
if (ret != 0)
return false;
- ret = HP303BSensor.measurePressureOnce(p, oversampling);
+ ret = HP303BSensor.measurePressureOnce(p, bhp303b_sensor.oversampling);
if (ret != 0)
return false;
@@ -71,22 +72,22 @@ bool HP303B_Read(float &temperature, float &pressure, uint8_t hp303b_address)
void HP303B_Detect(void)
{
- for (uint32_t i = 0; i < sizeof(addresses); i++)
+ for (uint32_t i = 0; i < sizeof(bhp303b_addresses); i++)
{
- if (!I2cSetDevice(addresses[i]))
+ if (!I2cSetDevice(bhp303b_addresses[i]))
{
continue;
}
- HP303BSensor.begin(addresses[i]);
+ HP303BSensor.begin(bhp303b_addresses[i]);
float t;
float p;
- if (HP303B_Read(t, p, addresses[i]))
+ if (HP303B_Read(t, p, bhp303b_addresses[i]))
{
- I2cSetActiveFound(addresses[i], types);
- address = addresses[i];
- type = 1;
+ I2cSetActiveFound(bhp303b_addresses[i], bhp303b_sensor.types);
+ bhp303b_sensor.address = bhp303b_addresses[i];
+ bhp303b_sensor.type = 1;
break;
}
}
@@ -95,12 +96,12 @@ void HP303B_Detect(void)
void HP303B_Show(bool json)
{
- if (HP303B_Read(temperature, pressure, address))
+ if (HP303B_Read(bhp303b_sensor.temperature, bhp303b_sensor.pressure, bhp303b_sensor.address))
{
char str_temperature[33];
- dtostrfd(temperature, Settings.flag2.temperature_resolution, str_temperature);
+ dtostrfd(bhp303b_sensor.temperature, Settings.flag2.temperature_resolution, str_temperature);
char str_pressure[33];
- dtostrfd(pressure, Settings.flag2.pressure_resolution, str_pressure);
+ dtostrfd(bhp303b_sensor.pressure, Settings.flag2.pressure_resolution, str_pressure);
if (json)
{
@@ -109,7 +110,7 @@ void HP303B_Show(bool json)
#ifdef USE_DOMOTICZ
if (0 == tele_period)
{
- DomoticzSensor(DZ_TEMP, temperature);
+ DomoticzSensor(DZ_TEMP, bhp303b_sensor.temperature);
}
#endif // USE_DOMOTICZ
#ifdef USE_WEBSERVER
@@ -140,7 +141,7 @@ bool Xsns73(uint8_t function)
{
HP303B_Detect();
}
- else if (type)
+ else if (bhp303b_sensor.type)
{
switch (function)
{
From abfa4f4fcd8fb24b5d47a8a599cadd5acd8e5bda Mon Sep 17 00:00:00 2001
From: Robert Jaakke
Date: Mon, 8 Jun 2020 10:19:51 +0200
Subject: [PATCH 06/35] refactored implementation
---
tasmota/xsns_73_hp303b.ino | 50 +++++++++++++++++++++-----------------
1 file changed, 28 insertions(+), 22 deletions(-)
diff --git a/tasmota/xsns_73_hp303b.ino b/tasmota/xsns_73_hp303b.ino
index 5a16ca905..ad439e555 100644
--- a/tasmota/xsns_73_hp303b.ino
+++ b/tasmota/xsns_73_hp303b.ino
@@ -44,12 +44,14 @@ struct BHP303B {
float temperature;
float pressure;
int16_t oversampling = 7;
- char types[7] = "HP303B";
+ char name[7] = "HP303B";
} bhp303b_sensor;
/*********************************************************************************************/
-bool HP303B_Read(float &temperature, float &pressure, uint8_t hp303b_address)
+bool HP303B_Read()
{
+ if (bhp303b_sensor.valid) { bhp303b_sensor.valid--; }
+
float t;
float p;
int16_t ret;
@@ -62,9 +64,10 @@ bool HP303B_Read(float &temperature, float &pressure, uint8_t hp303b_address)
if (ret != 0)
return false;
- temperature = (float)ConvertTemp(t);
- pressure = (float)ConvertPressure(p) / 100; //conversion to hPa
+ bhp303b_sensor.temperature = (float)ConvertTemp(t);
+ bhp303b_sensor.pressure = (float)ConvertPressure(p) / 100; //conversion to hPa
+ bhp303b_sensor.valid = SENSOR_MAX_MISS;
return true;
}
@@ -74,29 +77,33 @@ void HP303B_Detect(void)
{
for (uint32_t i = 0; i < sizeof(bhp303b_addresses); i++)
{
- if (!I2cSetDevice(bhp303b_addresses[i]))
- {
- continue;
- }
+ if (I2cActive(bhp303b_addresses[i])) { return; }
- HP303BSensor.begin(bhp303b_addresses[i]);
+ bhp303b_sensor.address = bhp303b_addresses[i];
- float t;
- float p;
- if (HP303B_Read(t, p, bhp303b_addresses[i]))
+ HP303BSensor.begin( bhp303b_sensor.address);
+
+ if (HP303B_Read())
{
- I2cSetActiveFound(bhp303b_addresses[i], bhp303b_sensor.types);
- bhp303b_sensor.address = bhp303b_addresses[i];
+ I2cSetActiveFound(bhp303b_sensor.address, bhp303b_sensor.name);
bhp303b_sensor.type = 1;
break;
}
}
}
+void HP303B_EverySecond(void)
+{
+ if (uptime &1) {
+ if (!HP303B_Read()) {
+ AddLogMissed(bhp303b_sensor.name, bhp303b_sensor.valid);
+ }
+ }
+}
+
void HP303B_Show(bool json)
{
-
- if (HP303B_Read(bhp303b_sensor.temperature, bhp303b_sensor.pressure, bhp303b_sensor.address))
+ if (bhp303b_sensor.valid)
{
char str_temperature[33];
dtostrfd(bhp303b_sensor.temperature, Settings.flag2.temperature_resolution, str_temperature);
@@ -130,21 +137,20 @@ void HP303B_Show(bool json)
bool Xsns73(uint8_t function)
{
- if (!I2cEnabled(XI2C_52))
- {
- return false;
- }
+ if (!I2cEnabled(XI2C_52)) { return false; }
bool result = false;
- if (FUNC_INIT == function)
- {
+ if (FUNC_INIT == function) {
HP303B_Detect();
}
else if (bhp303b_sensor.type)
{
switch (function)
{
+ case FUNC_EVERY_SECOND:
+ HP303B_EverySecond();
+ break;
case FUNC_JSON_APPEND:
HP303B_Show(1);
break;
From 92643e89d023aafbfc4af8e09cc4de9771f5627b Mon Sep 17 00:00:00 2001
From: Robert Jaakke
Date: Mon, 8 Jun 2020 20:37:04 +0200
Subject: [PATCH 07/35] Added support for multiple i2c addresses
---
lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp | 32 +++----
lib/LOLIN_HP303B/src/LOLIN_HP303B.h | 10 +--
tasmota/xsns_73_hp303b.ino | 115 ++++++++++++++------------
3 files changed, 84 insertions(+), 73 deletions(-)
diff --git a/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp b/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp
index fdeebd6a0..7f7f60a3f 100644
--- a/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp
+++ b/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp
@@ -35,7 +35,7 @@ LOLIN_HP303B::~LOLIN_HP303B(void)
* &bus: I2CBus which connects MC to HP303B
* slaveAddress: Address of the HP303B (0x77 or 0x76)
*/
-void LOLIN_HP303B::begin(TwoWire &bus, uint8_t slaveAddress)
+uint8_t LOLIN_HP303B::begin(TwoWire &bus, uint8_t slaveAddress)
{
//this flag will show if the initialization was successful
m_initFail = 0U;
@@ -50,20 +50,20 @@ void LOLIN_HP303B::begin(TwoWire &bus, uint8_t slaveAddress)
delay(50); //startup time of HP303B
- init();
+ return init();
}
-void LOLIN_HP303B::begin(uint8_t slaveAddress)
+uint8_t LOLIN_HP303B::begin(uint8_t slaveAddress)
{
- begin(Wire,slaveAddress);
+ return begin(Wire,slaveAddress);
}
/**
* SPI begin function for HP303B with 4-wire SPI
*/
-void LOLIN_HP303B::begin(SPIClass &bus, int32_t chipSelect)
+uint8_t LOLIN_HP303B::begin(SPIClass &bus, int32_t chipSelect)
{
- begin(bus, chipSelect, 0U);
+ return begin(bus, chipSelect, 0U);
}
/**
@@ -74,7 +74,7 @@ void LOLIN_HP303B::begin(SPIClass &bus, int32_t chipSelect)
* threeWire: 1 if HP303B is connected with 3-wire SPI
* 0 if HP303B is connected with 4-wire SPI (standard)
*/
-void LOLIN_HP303B::begin(SPIClass &bus, int32_t chipSelect, uint8_t threeWire)
+uint8_t LOLIN_HP303B::begin(SPIClass &bus, int32_t chipSelect, uint8_t threeWire)
{
//this flag will show if the initialization was successful
m_initFail = 0U;
@@ -102,11 +102,11 @@ void LOLIN_HP303B::begin(SPIClass &bus, int32_t chipSelect, uint8_t threeWire)
if(writeByte(HP303B__REG_ADR_SPI3W, HP303B__REG_CONTENT_SPI3W))
{
m_initFail = 1U;
- return;
+ return 0U;
}
}
- init();
+ return init();
}
/**
@@ -840,14 +840,14 @@ int16_t LOLIN_HP303B::correctTemp(void)
* This function has to be called from begin()
* and requires a valid bus initialization.
*/
-void LOLIN_HP303B::init(void)
+uint8_t LOLIN_HP303B::init(void)
{
int16_t prodId = readByteBitfield(HP303B__REG_INFO_PROD_ID);
if(prodId != HP303B__PROD_ID)
{
//Connected device is not a HP303B
m_initFail = 1U;
- return;
+ return 0U;
}
m_productID = prodId;
@@ -855,7 +855,7 @@ void LOLIN_HP303B::init(void)
if(revId < 0)
{
m_initFail = 1U;
- return;
+ return 0U;
}
m_revisionID = revId;
@@ -864,7 +864,7 @@ void LOLIN_HP303B::init(void)
if(sensor < 0)
{
m_initFail = 1U;
- return;
+ return 0U;
}
//...and use this sensor for temperature measurement
@@ -872,14 +872,14 @@ void LOLIN_HP303B::init(void)
if(writeByteBitfield((uint8_t)sensor, HP303B__REG_INFO_TEMP_SENSOR) < 0)
{
m_initFail = 1U;
- return;
+ return 0U;
}
//read coefficients
if(readcoeffs() < 0)
{
m_initFail = 1U;
- return;
+ return 0U;
}
//set to standby for further configuration
@@ -901,6 +901,8 @@ void LOLIN_HP303B::init(void)
// Fix IC with a fuse bit problem, which lead to a wrong temperature
// Should not affect ICs without this problem
correctTemp();
+
+ return 1U;
}
diff --git a/lib/LOLIN_HP303B/src/LOLIN_HP303B.h b/lib/LOLIN_HP303B/src/LOLIN_HP303B.h
index ce65e17f1..651b80380 100644
--- a/lib/LOLIN_HP303B/src/LOLIN_HP303B.h
+++ b/lib/LOLIN_HP303B/src/LOLIN_HP303B.h
@@ -19,10 +19,10 @@ public:
//destructor
~LOLIN_HP303B(void);
//begin
- void begin(TwoWire &bus, uint8_t slaveAddress);
- void begin(uint8_t slaveAddress=HP303B__STD_SLAVE_ADDRESS);
- void begin(SPIClass &bus, int32_t chipSelect);
- void begin(SPIClass &bus, int32_t chipSelect, uint8_t threeWire);
+ uint8_t begin(TwoWire &bus, uint8_t slaveAddress);
+ uint8_t begin(uint8_t slaveAddress=HP303B__STD_SLAVE_ADDRESS);
+ uint8_t begin(SPIClass &bus, int32_t chipSelect);
+ uint8_t begin(SPIClass &bus, int32_t chipSelect, uint8_t threeWire);
//end
void end(void);
@@ -115,7 +115,7 @@ private:
uint8_t m_threeWire;
//measurement
- void init(void);
+ uint8_t init(void);
int16_t readcoeffs(void);
int16_t setOpMode(uint8_t background, uint8_t temperature, uint8_t pressure);
int16_t setOpMode(uint8_t opMode);
diff --git a/tasmota/xsns_73_hp303b.ino b/tasmota/xsns_73_hp303b.ino
index ad439e555..b56fa6ded 100644
--- a/tasmota/xsns_73_hp303b.ino
+++ b/tasmota/xsns_73_hp303b.ino
@@ -29,45 +29,47 @@
#define XSNS_73 73
#define XI2C_52 52 // See I2CDEVICES.md
-#define HP303B_ADDR1 0x77
-#define HP303B_ADDR2 0x76
-
#include
-// HP303B Opject
+// HP303B Object
LOLIN_HP303B HP303BSensor = LOLIN_HP303B();
-uint8_t bhp303b_addresses[2] = {HP303B_ADDR1, HP303B_ADDR2};
+
+#define HP303B_MAX_SENSORS 2
+#define HP303B_START_ADDRESS 0x76
+
+struct {
+char types[7] = "HP303B";
+uint8_t count = 0;
+int16_t oversampling = 7;
+} hp303b_cfg;
struct BHP303B {
uint8_t address;
- uint8_t type = 0;
uint8_t valid = 0;
- float temperature;
- float pressure;
- int16_t oversampling = 7;
- char name[7] = "HP303B";
-} bhp303b_sensor;
+ float temperature = NAN;
+ float pressure = NAN;
+} hp303b_sensor[HP303B_MAX_SENSORS];
/*********************************************************************************************/
-bool HP303B_Read()
+bool HP303B_Read(uint8_t hp303b_idx)
{
- if (bhp303b_sensor.valid) { bhp303b_sensor.valid--; }
+ if (hp303b_sensor[hp303b_idx].valid) { hp303b_sensor[hp303b_idx].valid--; }
float t;
float p;
int16_t ret;
- ret = HP303BSensor.measureTempOnce(t, bhp303b_sensor.oversampling);
+ ret = HP303BSensor.measureTempOnce(t, hp303b_cfg.oversampling);
if (ret != 0)
return false;
- ret = HP303BSensor.measurePressureOnce(p, bhp303b_sensor.oversampling);
+ ret = HP303BSensor.measurePressureOnce(p, hp303b_cfg.oversampling);
if (ret != 0)
return false;
- bhp303b_sensor.temperature = (float)ConvertTemp(t);
- bhp303b_sensor.pressure = (float)ConvertPressure(p) / 100; //conversion to hPa
+ hp303b_sensor[hp303b_idx].temperature = (float)ConvertTemp(t);
+ hp303b_sensor[hp303b_idx].pressure = (float)ConvertPressure(p) / 100; //conversion to hPa
- bhp303b_sensor.valid = SENSOR_MAX_MISS;
+ hp303b_sensor[hp303b_idx].valid = SENSOR_MAX_MISS;
return true;
}
@@ -75,18 +77,15 @@ bool HP303B_Read()
void HP303B_Detect(void)
{
- for (uint32_t i = 0; i < sizeof(bhp303b_addresses); i++)
+ for (uint32_t i = 0; i < HP303B_MAX_SENSORS; i++)
{
- if (I2cActive(bhp303b_addresses[i])) { return; }
+ if (I2cActive(HP303B_START_ADDRESS + i)) { return; }
- bhp303b_sensor.address = bhp303b_addresses[i];
-
- HP303BSensor.begin( bhp303b_sensor.address);
-
- if (HP303B_Read())
+ if (HP303BSensor.begin(HP303B_START_ADDRESS + i))
{
- I2cSetActiveFound(bhp303b_sensor.address, bhp303b_sensor.name);
- bhp303b_sensor.type = 1;
+ hp303b_sensor[hp303b_cfg.count].address = HP303B_START_ADDRESS + i;
+ I2cSetActiveFound(hp303b_sensor[hp303b_cfg.count].address, hp303b_cfg.types);
+ hp303b_cfg.count++;
break;
}
}
@@ -94,39 +93,49 @@ void HP303B_Detect(void)
void HP303B_EverySecond(void)
{
- if (uptime &1) {
- if (!HP303B_Read()) {
- AddLogMissed(bhp303b_sensor.name, bhp303b_sensor.valid);
+ for (uint32_t i = 0; i < hp303b_cfg.count; i++) {
+ if (uptime &1) {
+ if (!HP303B_Read(i)) {
+ AddLogMissed(hp303b_cfg.types, hp303b_sensor[i].valid);
}
}
+ }
}
void HP303B_Show(bool json)
{
- if (bhp303b_sensor.valid)
- {
- char str_temperature[33];
- dtostrfd(bhp303b_sensor.temperature, Settings.flag2.temperature_resolution, str_temperature);
- char str_pressure[33];
- dtostrfd(bhp303b_sensor.pressure, Settings.flag2.pressure_resolution, str_pressure);
-
- if (json)
- {
- ResponseAppend_P(PSTR(",\"HP303B\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_PRESSURE "\":%s"), str_temperature, str_pressure);
- ResponseJsonEnd();
-#ifdef USE_DOMOTICZ
- if (0 == tele_period)
- {
- DomoticzSensor(DZ_TEMP, bhp303b_sensor.temperature);
- }
-#endif // USE_DOMOTICZ
-#ifdef USE_WEBSERVER
+ for (uint32_t i = 0; i < hp303b_cfg.count; i++) {
+ char sensor_name[12];
+ strlcpy(sensor_name, hp303b_cfg.types, sizeof(sensor_name));
+ if (hp303b_cfg.count > 1) {
+ snprintf_P(sensor_name, sizeof(sensor_name), PSTR("%s%c0x%02X"), sensor_name, IndexSeparator(), hp303b_sensor[i].address); // MCP9808-18, MCP9808-1A etc.
}
- else
+
+ if (hp303b_sensor[i].valid)
{
- WSContentSend_PD(HTTP_SNS_TEMP, "HP303B", str_temperature, TempUnit());
- WSContentSend_PD(HTTP_SNS_PRESSURE, "HP303B", str_pressure, PressureUnit().c_str());
-#endif // USE_WEBSERVER
+ char str_temperature[33];
+ dtostrfd(hp303b_sensor[i].temperature, Settings.flag2.temperature_resolution, str_temperature);
+ char str_pressure[33];
+ dtostrfd(hp303b_sensor[i].pressure, Settings.flag2.pressure_resolution, str_pressure);
+
+ if (json)
+ {
+ ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_PRESSURE "\":%s"), sensor_name, str_temperature, str_pressure);
+ ResponseJsonEnd();
+ #ifdef USE_DOMOTICZ
+ if (0 == tele_period)
+ {
+ DomoticzSensor(DZ_TEMP, hp303b_sensor[i].temperature);
+ }
+ #endif // USE_DOMOTICZ
+ #ifdef USE_WEBSERVER
+ }
+ else
+ {
+ WSContentSend_PD(HTTP_SNS_TEMP, sensor_name, str_temperature, TempUnit());
+ WSContentSend_PD(HTTP_SNS_PRESSURE, sensor_name, str_pressure, PressureUnit().c_str());
+ #endif // USE_WEBSERVER
+ }
}
}
}
@@ -144,7 +153,7 @@ bool Xsns73(uint8_t function)
if (FUNC_INIT == function) {
HP303B_Detect();
}
- else if (bhp303b_sensor.type)
+ else if (hp303b_cfg.count)
{
switch (function)
{
From 9d2d22558c9150a23eb86a88ba4fad0a4a9a22a5 Mon Sep 17 00:00:00 2001
From: Robert Jaakke
Date: Tue, 9 Jun 2020 09:14:44 +0200
Subject: [PATCH 08/35] Resolved review comments
---
tasmota/xsns_73_hp303b.ino | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/tasmota/xsns_73_hp303b.ino b/tasmota/xsns_73_hp303b.ino
index b56fa6ded..01145e0d1 100644
--- a/tasmota/xsns_73_hp303b.ino
+++ b/tasmota/xsns_73_hp303b.ino
@@ -79,14 +79,13 @@ void HP303B_Detect(void)
{
for (uint32_t i = 0; i < HP303B_MAX_SENSORS; i++)
{
- if (I2cActive(HP303B_START_ADDRESS + i)) { return; }
+ if (!I2cSetDevice(HP303B_START_ADDRESS + i )) { continue; }
if (HP303BSensor.begin(HP303B_START_ADDRESS + i))
{
hp303b_sensor[hp303b_cfg.count].address = HP303B_START_ADDRESS + i;
I2cSetActiveFound(hp303b_sensor[hp303b_cfg.count].address, hp303b_cfg.types);
hp303b_cfg.count++;
- break;
}
}
}
@@ -108,7 +107,7 @@ void HP303B_Show(bool json)
char sensor_name[12];
strlcpy(sensor_name, hp303b_cfg.types, sizeof(sensor_name));
if (hp303b_cfg.count > 1) {
- snprintf_P(sensor_name, sizeof(sensor_name), PSTR("%s%c0x%02X"), sensor_name, IndexSeparator(), hp303b_sensor[i].address); // MCP9808-18, MCP9808-1A etc.
+ snprintf_P(sensor_name, sizeof(sensor_name), PSTR("%s%c0x%02X"), sensor_name, IndexSeparator(), hp303b_sensor[i].address); // HP303B-0x76, HP303B-0x77
}
if (hp303b_sensor[i].valid)
@@ -123,8 +122,8 @@ void HP303B_Show(bool json)
ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_PRESSURE "\":%s"), sensor_name, str_temperature, str_pressure);
ResponseJsonEnd();
#ifdef USE_DOMOTICZ
- if (0 == tele_period)
- {
+ // Domoticz and knx only support one temp sensor
+ if ((0 == tele_period) && (0 == i)) {
DomoticzSensor(DZ_TEMP, hp303b_sensor[i].temperature);
}
#endif // USE_DOMOTICZ
From 325564fbc7da74211b70b166f27bcea9d832a022 Mon Sep 17 00:00:00 2001
From: Robert Jaakke
Date: Wed, 10 Jun 2020 10:03:02 +0200
Subject: [PATCH 09/35] Added option to set i2c address in measure...Once in
lib
---
lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp | 46 ++++++++++++++++++++++++---
lib/LOLIN_HP303B/src/LOLIN_HP303B.h | 6 ++--
tasmota/xsns_73_hp303b.ino | 4 +--
3 files changed, 48 insertions(+), 8 deletions(-)
diff --git a/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp b/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp
index 7f7f60a3f..b73e12b83 100644
--- a/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp
+++ b/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp
@@ -183,7 +183,23 @@ int16_t LOLIN_HP303B::standby(void)
*/
int16_t LOLIN_HP303B::measureTempOnce(float &result)
{
- return measureTempOnce(result, m_tempOsr);
+ return measureTempOnce(result, m_slaveAddress, m_tempOsr);
+}
+
+/**
+ * performs one temperature measurement and writes result to the given address
+ *
+ * &result: reference to a 32-Bit signed Integer value where the result will be written
+ * It will not be written if result==NULL
+ * returns: 0 on success
+ * -4 if the HP303B is could not finish its measurement in time
+ * -3 if the HP303B is already busy
+ * -2 if the object initialization failed
+ * -1 on other fail
+ */
+int16_t LOLIN_HP303B::measureTempOnce(float &result, uint8_t slaveAddress)
+{
+ return measureTempOnce(result, slaveAddress, m_tempOsr);
}
/**
@@ -202,8 +218,11 @@ int16_t LOLIN_HP303B::measureTempOnce(float &result)
* -2 if the object initialization failed
* -1 on other fail
*/
-int16_t LOLIN_HP303B::measureTempOnce(float &result, uint8_t oversamplingRate)
+int16_t LOLIN_HP303B::measureTempOnce(float &result, uint8_t slaveAddress, uint8_t oversamplingRate)
{
+ //Set I2C bus connection
+ m_slaveAddress = slaveAddress;
+
//Start measurement
int16_t ret = startMeasureTempOnce(oversamplingRate);
if(ret!=HP303B__SUCCEEDED)
@@ -288,7 +307,23 @@ int16_t LOLIN_HP303B::startMeasureTempOnce(uint8_t oversamplingRate)
*/
int16_t LOLIN_HP303B::measurePressureOnce(float &result)
{
- return measurePressureOnce(result, m_prsOsr);
+ return measurePressureOnce(result, m_slaveAddress, m_prsOsr);
+}
+
+/**
+ * performs one pressure measurement and writes result to the given address
+ *
+ * &result: reference to a 32-Bit signed Integer value where the result will be written
+ * It will not be written if result==NULL
+ * returns: 0 on success
+ * -4 if the HP303B is could not finish its measurement in time
+ * -3 if the HP303B is already busy
+ * -2 if the object initialization failed
+ * -1 on other fail
+ */
+int16_t LOLIN_HP303B::measurePressureOnce(float &result, uint8_t slaveAddress)
+{
+ return measurePressureOnce(result, slaveAddress, m_prsOsr);
}
/**
@@ -307,8 +342,11 @@ int16_t LOLIN_HP303B::measurePressureOnce(float &result)
* -2 if the object initialization failed
* -1 on other fail
*/
-int16_t LOLIN_HP303B::measurePressureOnce(float &result, uint8_t oversamplingRate)
+int16_t LOLIN_HP303B::measurePressureOnce(float &result, uint8_t slaveAddress, uint8_t oversamplingRate)
{
+ //Set I2C bus connection
+ m_slaveAddress = slaveAddress;
+
//start the measurement
int16_t ret = startMeasurePressureOnce(oversamplingRate);
if(ret != HP303B__SUCCEEDED)
diff --git a/lib/LOLIN_HP303B/src/LOLIN_HP303B.h b/lib/LOLIN_HP303B/src/LOLIN_HP303B.h
index 651b80380..4d04ff6da 100644
--- a/lib/LOLIN_HP303B/src/LOLIN_HP303B.h
+++ b/lib/LOLIN_HP303B/src/LOLIN_HP303B.h
@@ -35,11 +35,13 @@ public:
//Command Mode
int16_t measureTempOnce(float &result);
- int16_t measureTempOnce(float &result, uint8_t oversamplingRate);
+ int16_t measureTempOnce(float &result, uint8_t slaveAddress);
+ int16_t measureTempOnce(float &result, uint8_t slaveAddress, uint8_t oversamplingRate);
int16_t startMeasureTempOnce(void);
int16_t startMeasureTempOnce(uint8_t oversamplingRate);
int16_t measurePressureOnce(float &result);
- int16_t measurePressureOnce(float &result, uint8_t oversamplingRate);
+ int16_t measurePressureOnce(float &result, uint8_t slaveAddress);
+ int16_t measurePressureOnce(float &result, uint8_t slaveAddress, uint8_t oversamplingRate);
int16_t startMeasurePressureOnce(void);
int16_t startMeasurePressureOnce(uint8_t oversamplingRate);
int16_t getSingleResult(float &result);
diff --git a/tasmota/xsns_73_hp303b.ino b/tasmota/xsns_73_hp303b.ino
index 01145e0d1..1be0a1606 100644
--- a/tasmota/xsns_73_hp303b.ino
+++ b/tasmota/xsns_73_hp303b.ino
@@ -58,11 +58,11 @@ bool HP303B_Read(uint8_t hp303b_idx)
float p;
int16_t ret;
- ret = HP303BSensor.measureTempOnce(t, hp303b_cfg.oversampling);
+ ret = HP303BSensor.measureTempOnce(t, hp303b_sensor[hp303b_idx].address, hp303b_cfg.oversampling);
if (ret != 0)
return false;
- ret = HP303BSensor.measurePressureOnce(p, hp303b_cfg.oversampling);
+ ret = HP303BSensor.measurePressureOnce(p, hp303b_sensor[hp303b_idx].address, hp303b_cfg.oversampling);
if (ret != 0)
return false;
From 1d68fe9bc6e770f0f3ac590b98cfcadfe410a52e Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Wed, 10 Jun 2020 20:14:46 +0200
Subject: [PATCH 10/35] Cleaned TLS options and prepare for TELEGRAM
---
tasmota/StackThunk_light.cpp | 2 +-
tasmota/WiFiClientSecureLightBearSSL.cpp | 1827 +++++++++++-----------
tasmota/WiFiClientSecureLightBearSSL.h | 442 +++---
tasmota/my_user_config.h | 12 +
tasmota/tasmota.ino | 4 +-
tasmota/tasmota_ca.ino | 93 +-
tasmota/tasmota_globals.h | 2 +-
7 files changed, 1223 insertions(+), 1159 deletions(-)
mode change 100644 => 100755 tasmota/WiFiClientSecureLightBearSSL.cpp
mode change 100644 => 100755 tasmota/WiFiClientSecureLightBearSSL.h
diff --git a/tasmota/StackThunk_light.cpp b/tasmota/StackThunk_light.cpp
index 5dcc20d62..c9f9bc78e 100644
--- a/tasmota/StackThunk_light.cpp
+++ b/tasmota/StackThunk_light.cpp
@@ -40,7 +40,7 @@ uint32_t *stack_thunk_light_save = NULL; /* Saved A1 while in BearSSL */
uint32_t stack_thunk_light_refcnt = 0;
//#define _stackSize (5600/4)
-#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_TLS_FORCE_EC_CIPHER)
+#ifdef USE_MQTT_TLS_FORCE_EC_CIPHER
#define _stackSize (5300/4) // using a light version of bearssl we can save 300 bytes
#else
#define _stackSize (3600/4) // using a light version of bearssl we can save 2k
diff --git a/tasmota/WiFiClientSecureLightBearSSL.cpp b/tasmota/WiFiClientSecureLightBearSSL.cpp
old mode 100644
new mode 100755
index 434522b14..d0907788e
--- a/tasmota/WiFiClientSecureLightBearSSL.cpp
+++ b/tasmota/WiFiClientSecureLightBearSSL.cpp
@@ -1,912 +1,915 @@
-/*
- WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries
- - Mostly compatible with Arduino WiFi shield library and standard
- WiFiClient/ServerSecure (except for certificate handling).
-
- Copyright (c) 2018 Earle F. Philhower, III
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-*/
-
-#include "my_user_config.h"
-//#ifdef USE_MQTT_TLS
-#if defined ESP8266 && (defined(USE_MQTT_TLS) || defined (USE_SENDMAIL))
-
-//#define DEBUG_TLS
-
-#define LWIP_INTERNAL
-
-#include
-#include
-#include
-
-extern "C" {
-#include "osapi.h"
-#include "ets_sys.h"
-}
-#include "debug.h"
-#include "WiFiClientSecureLightBearSSL.h" // needs to be before "ESP8266WiFi.h" to avoid conflict with Arduino headers
-#include "ESP8266WiFi.h"
-#include "WiFiClient.h"
-#include "StackThunk_light.h"
-#include "lwip/opt.h"
-#include "lwip/ip.h"
-#include "lwip/tcp.h"
-#include "lwip/inet.h"
-#include "lwip/netif.h"
-#include
-#include "c_types.h"
-
-#include
-#ifndef ARDUINO_ESP8266_RELEASE_2_5_2
-#undef DEBUG_TLS
-#endif
-
-#ifdef DEBUG_TLS
-#include "coredecls.h"
-#define LOG_HEAP_SIZE(a) _Log_heap_size(a)
-void _Log_heap_size(const char *msg) {
- register uint32_t *sp asm("a1");
- int freestack = 4 * (sp - g_pcont->stack);
- Serial.printf("%s %d, Fragmentation=%d, Thunkstack=%d, Free stack=%d, FreeContStack=%d\n",
- msg, ESP.getFreeHeap(), ESP.getHeapFragmentation(), stack_thunk_light_get_max_usage(),
- freestack, ESP.getFreeContStack());
-}
-#else
-#define LOG_HEAP_SIZE(a)
-#endif
-
-// Stack thunked versions of calls
-// Initially in BearSSLHelpers.h
-extern "C" {
-extern unsigned char *thunk_light_br_ssl_engine_recvapp_buf( const br_ssl_engine_context *cc, size_t *len);
-extern void thunk_light_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len);
-extern unsigned char *thunk_light_br_ssl_engine_recvrec_buf( const br_ssl_engine_context *cc, size_t *len);
-extern void thunk_light_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len);
-extern unsigned char *thunk_light_br_ssl_engine_sendapp_buf( const br_ssl_engine_context *cc, size_t *len);
-extern void thunk_light_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len);
-extern unsigned char *thunk_light_br_ssl_engine_sendrec_buf( const br_ssl_engine_context *cc, size_t *len);
-extern void thunk_light_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len);
-};
-
-// Second stack thunked helpers
-make_stack_thunk_light(br_ssl_engine_recvapp_ack);
-make_stack_thunk_light(br_ssl_engine_recvapp_buf);
-make_stack_thunk_light(br_ssl_engine_recvrec_ack);
-make_stack_thunk_light(br_ssl_engine_recvrec_buf);
-make_stack_thunk_light(br_ssl_engine_sendapp_ack);
-make_stack_thunk_light(br_ssl_engine_sendapp_buf);
-make_stack_thunk_light(br_ssl_engine_sendrec_ack);
-make_stack_thunk_light(br_ssl_engine_sendrec_buf);
-
-// create new version of Thunk function to store on SYS stack
-// unless the Thunk was initialized. Thanks to AES128 GCM, we can keep
-// symetric processing on the stack
-void min_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len) {
- if (stack_thunk_light_get_refcnt()) {
- return thunk_light_br_ssl_engine_recvapp_ack(cc, len);
- } else {
- return br_ssl_engine_recvapp_ack(cc, len);
- }
-}
-unsigned char *min_br_ssl_engine_recvapp_buf(const br_ssl_engine_context *cc, size_t *len) {
- if (stack_thunk_light_get_refcnt()) {
- return thunk_light_br_ssl_engine_recvapp_buf(cc, len);
- } else {
- return br_ssl_engine_recvapp_buf(cc, len);
- }
-}
-void min_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len) {
- if (stack_thunk_light_get_refcnt()) {
- return thunk_light_br_ssl_engine_recvrec_ack(cc, len);
- } else {
- return br_ssl_engine_recvrec_ack(cc, len);
- }
-}
-unsigned char *min_br_ssl_engine_recvrec_buf(const br_ssl_engine_context *cc, size_t *len) {
- if (stack_thunk_light_get_refcnt()) {
- return thunk_light_br_ssl_engine_recvrec_buf(cc, len);
- } else {
- return br_ssl_engine_recvrec_buf(cc, len);
- }
-}
-void min_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len) {
- if (stack_thunk_light_get_refcnt()) {
- return thunk_light_br_ssl_engine_sendapp_ack(cc, len);
- } else {
- return br_ssl_engine_sendapp_ack(cc, len);
- }
-}
-unsigned char *min_br_ssl_engine_sendapp_buf(const br_ssl_engine_context *cc, size_t *len) {
- if (stack_thunk_light_get_refcnt()) {
- return thunk_light_br_ssl_engine_sendapp_buf(cc, len);
- } else {
- return br_ssl_engine_sendapp_buf(cc, len);
- }
-}
-void min_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len) {
- if (stack_thunk_light_get_refcnt()) {
- return thunk_light_br_ssl_engine_sendrec_ack(cc, len);
- } else {
- return br_ssl_engine_sendrec_ack(cc, len);
- }
-}
-unsigned char *min_br_ssl_engine_sendrec_buf(const br_ssl_engine_context *cc, size_t *len) {
- if (stack_thunk_light_get_refcnt()) {
- return thunk_light_br_ssl_engine_sendrec_buf(cc, len);
- } else {
- return br_ssl_engine_sendrec_buf(cc, len);
- }
-}
-
-// Use min_ instead of original thunk_
-#define br_ssl_engine_recvapp_ack min_br_ssl_engine_recvapp_ack
-#define br_ssl_engine_recvapp_buf min_br_ssl_engine_recvapp_buf
-#define br_ssl_engine_recvrec_ack min_br_ssl_engine_recvrec_ack
-#define br_ssl_engine_recvrec_buf min_br_ssl_engine_recvrec_buf
-#define br_ssl_engine_sendapp_ack min_br_ssl_engine_sendapp_ack
-#define br_ssl_engine_sendapp_buf min_br_ssl_engine_sendapp_buf
-#define br_ssl_engine_sendrec_ack min_br_ssl_engine_sendrec_ack
-#define br_ssl_engine_sendrec_buf min_br_ssl_engine_sendrec_buf
-
-//#define DEBUG_ESP_SSL
-#ifdef DEBUG_ESP_SSL
-#define DEBUG_BSSL(fmt, ...) DEBUG_ESP_PORT.printf_P((PGM_P)PSTR( "BSSL:" fmt), ## __VA_ARGS__)
-//#define DEBUG_BSSL(fmt, ...) Serial.printf(fmt, ## __VA_ARGS__)
-#else
-#define DEBUG_BSSL(...)
-#endif
-
-namespace BearSSL {
-
-void WiFiClientSecure_light::_clear() {
- // TLS handshake may take more than the 5 second default timeout
- _timeout = 10000; // 10 seconds max, it should never go over 6 seconds
-
- _sc = nullptr;
- _ctx_present = false;
- _eng = nullptr;
- _iobuf_in = nullptr;
- _iobuf_out = nullptr;
- _now = 0; // You can override or ensure time() is correct w/configTime
- setBufferSizes(1024, 1024); // reasonable minimum
- _handshake_done = false;
- _last_error = 0;
- _recvapp_buf = nullptr;
- _recvapp_len = 0;
- _fingerprint_any = true; // by default accept all fingerprints
- _fingerprint1 = nullptr;
- _fingerprint2 = nullptr;
- _chain_P = nullptr;
- _sk_ec_P = nullptr;
- _ta_P = nullptr;
- _max_thunkstack_use = 0;
-}
-
-// Constructor
-WiFiClientSecure_light::WiFiClientSecure_light(int recv, int xmit) : WiFiClient() {
- _clear();
-LOG_HEAP_SIZE("StackThunk before");
- //stack_thunk_light_add_ref();
-LOG_HEAP_SIZE("StackThunk after");
- // now finish the setup
- setBufferSizes(recv, xmit); // reasonable minimum
- allocateBuffers();
-}
-
-WiFiClientSecure_light::~WiFiClientSecure_light() {
- if (_client) {
- _client->unref();
- _client = nullptr;
- }
- //_cipher_list = nullptr; // std::shared will free if last reference
- _freeSSL();
-}
-
-void WiFiClientSecure_light::allocateBuffers(void) {
- // We prefer to allocate all buffers at start, rather than lazy allocation and deallocation
- // in the long run it avoids heap fragmentation and improves stability
- LOG_HEAP_SIZE("allocateBuffers before");
- _sc = std::make_shared();
- LOG_HEAP_SIZE("allocateBuffers ClientContext");
- _iobuf_in = std::shared_ptr(new unsigned char[_iobuf_in_size], std::default_delete());
- _iobuf_out = std::shared_ptr(new unsigned char[_iobuf_out_size], std::default_delete());
- LOG_HEAP_SIZE("allocateBuffers after");
-}
-
-void WiFiClientSecure_light::setClientECCert(const br_x509_certificate *cert, const br_ec_private_key *sk,
- unsigned allowed_usages, unsigned cert_issuer_key_type) {
- _chain_P = cert;
- _sk_ec_P = sk;
- _allowed_usages = allowed_usages;
- _cert_issuer_key_type = cert_issuer_key_type;
-}
-
-void WiFiClientSecure_light::setTrustAnchor(const br_x509_trust_anchor *ta) {
- _ta_P = ta;
-}
-
-void WiFiClientSecure_light::setBufferSizes(int recv, int xmit) {
- // Following constants taken from bearssl/src/ssl/ssl_engine.c (not exported unfortunately)
- const int MAX_OUT_OVERHEAD = 85;
- const int MAX_IN_OVERHEAD = 325;
-
- // The data buffers must be between 512B and 16KB
- recv = std::max(512, std::min(16384, recv));
- xmit = std::max(512, std::min(16384, xmit));
-
- // Add in overhead for SSL protocol
- recv += MAX_IN_OVERHEAD;
- xmit += MAX_OUT_OVERHEAD;
- _iobuf_in_size = recv;
- _iobuf_out_size = xmit;
-}
-
-bool WiFiClientSecure_light::stop(unsigned int maxWaitMs) {
-#ifdef ARDUINO_ESP8266_RELEASE_2_4_2
- WiFiClient::stop(); // calls our virtual flush()
- _freeSSL();
- return true;
-#else
- bool ret = WiFiClient::stop(maxWaitMs); // calls our virtual flush()
- _freeSSL();
- return ret;
-#endif
-}
-
-bool WiFiClientSecure_light::flush(unsigned int maxWaitMs) {
- (void) _run_until(BR_SSL_SENDAPP);
-#ifdef ARDUINO_ESP8266_RELEASE_2_4_2
- WiFiClient::flush();
-#else
- return WiFiClient::flush(maxWaitMs);
-#endif
-}
-
-int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port) {
- clearLastError();
- if (!WiFiClient::connect(ip, port)) {
- setLastError(ERR_TCP_CONNECT);
- return 0;
- }
- return _connectSSL(nullptr);
-}
-
-int WiFiClientSecure_light::connect(const char* name, uint16_t port) {
- IPAddress remote_addr;
- clearLastError();
- if (!WiFi.hostByName(name, remote_addr)) {
- DEBUG_BSSL("connect: Name loopup failure\n");
- setLastError(ERR_CANT_RESOLVE_IP);
- return 0;
- }
- if (!WiFiClient::connect(remote_addr, port)) {
- DEBUG_BSSL("connect: Unable to connect TCP socket\n");
- _last_error = ERR_TCP_CONNECT;
- return 0;
- }
- LOG_HEAP_SIZE("Before calling _connectSSL");
- return _connectSSL(name);
-}
-
-void WiFiClientSecure_light::_freeSSL() {
- _ctx_present = false;
- _recvapp_buf = nullptr;
- _recvapp_len = 0;
- // This connection is toast
- _handshake_done = false;
-}
-
-bool WiFiClientSecure_light::_clientConnected() {
- return (_client && _client->state() == ESTABLISHED);
-}
-
-uint8_t WiFiClientSecure_light::connected() {
- if (available() || (_clientConnected() && _handshake_done)) {
- return true;
- }
- return false;
-}
-
-size_t WiFiClientSecure_light::_write(const uint8_t *buf, size_t size, bool pmem) {
- size_t sent_bytes = 0;
-
- if (!connected() || !size || !_handshake_done) {
- return 0;
- }
-
- do {
- // Ensure we yield if we need multiple fragments to avoid WDT
- if (sent_bytes) {
- optimistic_yield(1000);
- }
-
- // Get BearSSL to a state where we can send
- if (_run_until(BR_SSL_SENDAPP) < 0) {
- break;
- }
-
- if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) {
- size_t sendapp_len;
- unsigned char *sendapp_buf = br_ssl_engine_sendapp_buf(_eng, &sendapp_len);
- int to_send = size > sendapp_len ? sendapp_len : size;
- if (pmem) {
- memcpy_P(sendapp_buf, buf, to_send);
- } else {
- memcpy(sendapp_buf, buf, to_send);
- }
- br_ssl_engine_sendapp_ack(_eng, to_send);
- br_ssl_engine_flush(_eng, 0);
- flush();
- buf += to_send;
- sent_bytes += to_send;
- size -= to_send;
- } else {
- break;
- }
- } while (size);
-
- LOG_HEAP_SIZE("_write");
- return sent_bytes;
-}
-
-size_t WiFiClientSecure_light::write(const uint8_t *buf, size_t size) {
- return _write(buf, size, false);
-}
-
-size_t WiFiClientSecure_light::write_P(PGM_P buf, size_t size) {
- return _write((const uint8_t *)buf, size, true);
-}
-
-// We have to manually read and send individual chunks.
-size_t WiFiClientSecure_light::write(Stream& stream) {
- size_t totalSent = 0;
- size_t countRead;
- size_t countSent;
-
- if (!connected() || !_handshake_done) {
- DEBUG_BSSL("write: Connect/handshake not completed yet\n");
- return 0;
- }
-
- do {
- uint8_t temp[256]; // Temporary chunk size same as ClientContext
- countSent = 0;
- countRead = stream.readBytes(temp, sizeof(temp));
- if (countRead) {
- countSent = _write((const uint8_t*)temp, countRead, true);
- totalSent += countSent;
- }
- yield(); // Feed the WDT
- } while ((countSent == countRead) && (countSent > 0));
- return totalSent;
-}
-
-int WiFiClientSecure_light::read(uint8_t *buf, size_t size) {
- if (!ctx_present() || !_handshake_done) {
- return -1;
- }
-
- int avail = available();
- bool conn = connected();
- if (!avail && conn) {
- return 0; // We're still connected, but nothing to read
- }
- if (!avail && !conn) {
- DEBUG_BSSL("read: Not connected, none left available\n");
- return -1;
- }
-
- if (avail) {
- // Take data from the recvapp buffer
- int to_copy = _recvapp_len < size ? _recvapp_len : size;
- memcpy(buf, _recvapp_buf, to_copy);
- br_ssl_engine_recvapp_ack(_eng, to_copy);
- _recvapp_buf = nullptr;
- _recvapp_len = 0;
- return to_copy;
- }
-
- if (!conn) {
- DEBUG_BSSL("read: Not connected\n");
- return -1;
- }
- return 0; // If we're connected, no error but no read.
-}
-
-int WiFiClientSecure_light::read() {
- uint8_t c;
- if (1 == read(&c, 1)) {
- return c;
- }
- DEBUG_BSSL("read: failed\n");
- return -1;
-}
-
-int WiFiClientSecure_light::available() {
- if (_recvapp_buf) {
- return _recvapp_len; // Anything from last call?
- }
- _recvapp_buf = nullptr;
- _recvapp_len = 0;
- if (!ctx_present() || _run_until(BR_SSL_RECVAPP, false) < 0) {
- return 0;
- }
- int st = br_ssl_engine_current_state(_eng);
- if (st == BR_SSL_CLOSED) {
- return 0; // Nothing leftover, SSL is closed
- }
- if (st & BR_SSL_RECVAPP) {
- _recvapp_buf = br_ssl_engine_recvapp_buf(_eng, &_recvapp_len);
- return _recvapp_len;
- }
-
- return 0;
-}
-
-int WiFiClientSecure_light::peek() {
- if (!ctx_present() || !available()) {
- DEBUG_BSSL("peek: Not connected, none left available\n");
- return -1;
- }
- if (_recvapp_buf && _recvapp_len) {
- return _recvapp_buf[0];
- }
- DEBUG_BSSL("peek: No data left\n");
- return -1;
-}
-
-size_t WiFiClientSecure_light::peekBytes(uint8_t *buffer, size_t length) {
- size_t to_copy = 0;
- if (!ctx_present()) {
- DEBUG_BSSL("peekBytes: Not connected\n");
- return 0;
- }
-
- _startMillis = millis();
- while ((available() < (int) length) && ((millis() - _startMillis) < 5000)) {
- yield();
- }
-
- to_copy = _recvapp_len < length ? _recvapp_len : length;
- memcpy(buffer, _recvapp_buf, to_copy);
- return to_copy;
-}
-
-/* --- Copied almost verbatim from BEARSSL SSL_IO.C ---
- Run the engine, until the specified target state is achieved, or
- an error occurs. The target state is SENDAPP, RECVAPP, or the
- combination of both (the combination matches either). When a match is
- achieved, this function returns 0. On error, it returns -1.
-*/
-int WiFiClientSecure_light::_run_until(unsigned target, bool blocking) {
-//LOG_HEAP_SIZE("_run_until 1");
- if (!ctx_present()) {
- DEBUG_BSSL("_run_until: Not connected\n");
- return -1;
- }
- for (int no_work = 0; blocking || no_work < 2;) {
- if (blocking) {
- // Only for blocking operations can we afford to yield()
- optimistic_yield(100);
- }
-
- int state;
- state = br_ssl_engine_current_state(_eng);
- if (state & BR_SSL_CLOSED) {
- return -1;
- }
-
- if (!(_client->state() == ESTABLISHED) && !WiFiClient::available()) {
- return (state & target) ? 0 : -1;
- }
-
- /*
- If there is some record data to send, do it. This takes
- precedence over everything else.
- */
- if (state & BR_SSL_SENDREC) {
- unsigned char *buf;
- size_t len;
- int wlen;
-
- buf = br_ssl_engine_sendrec_buf(_eng, &len);
- wlen = WiFiClient::write(buf, len);
- if (wlen <= 0) {
- /*
- If we received a close_notify and we
- still send something, then we have our
- own response close_notify to send, and
- the peer is allowed by RFC 5246 not to
- wait for it.
- */
- return -1;
- }
- if (wlen > 0) {
- br_ssl_engine_sendrec_ack(_eng, wlen);
- }
- no_work = 0;
- continue;
- }
-
- /*
- If we reached our target, then we are finished.
- */
- if (state & target) {
- return 0;
- }
- /*
- If some application data must be read, and we did not
- exit, then this means that we are trying to write data,
- and that's not possible until the application data is
- read. This may happen if using a shared in/out buffer,
- and the underlying protocol is not strictly half-duplex.
- This is unrecoverable here, so we report an error.
- */
- if (state & BR_SSL_RECVAPP) {
- DEBUG_BSSL("_run_until: Fatal protocol state\n");
- return -1;
- }
- /*
- If we reached that point, then either we are trying
- to read data and there is some, or the engine is stuck
- until a new record is obtained.
- */
- if (state & BR_SSL_RECVREC) {
- if (WiFiClient::available()) {
- unsigned char *buf;
- size_t len;
- int rlen;
-
- buf = br_ssl_engine_recvrec_buf(_eng, &len);
- rlen = WiFiClient::read(buf, len);
- if (rlen < 0) {
- return -1;
- }
- if (rlen > 0) {
- br_ssl_engine_recvrec_ack(_eng, rlen);
- }
- no_work = 0;
- continue;
- }
- }
- /*
- We can reach that point if the target RECVAPP, and
- the state contains SENDAPP only. This may happen with
- a shared in/out buffer. In that case, we must flush
- the buffered data to "make room" for a new incoming
- record.
- */
- br_ssl_engine_flush(_eng, 0);
-
- no_work++; // We didn't actually advance here
- }
- // We only get here if we ran through the loop without getting anything done
- return -1;
-}
-
-bool WiFiClientSecure_light::_wait_for_handshake() {
- _handshake_done = false;
- while (!_handshake_done && _clientConnected()) {
- int ret = _run_until(BR_SSL_SENDAPP);
- if (ret < 0) {
- DEBUG_BSSL("_wait_for_handshake: failed\n");
- break;
- }
- if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) {
- _handshake_done = true;
- }
- optimistic_yield(1000);
- }
- return _handshake_done;
-}
-
-static uint8_t htoi (unsigned char c)
-{
- if (c>='0' && c <='9') return c - '0';
- else if (c>='A' && c<='F') return 10 + c - 'A';
- else if (c>='a' && c<='f') return 10 + c - 'a';
- else return 255;
-}
-
-extern "C" {
-
- // see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c
- void tohex(unsigned char * in, size_t insz, char * out, size_t outsz) {
- unsigned char * pin = in;
- static const char * hex = "0123456789ABCDEF";
- char * pout = out;
- for(; pin < in+insz; pout +=3, pin++){
- pout[0] = hex[(*pin>>4) & 0xF];
- pout[1] = hex[ *pin & 0xF];
- pout[2] = ':';
- if (pout + 3 - out > outsz){
- /* Better to truncate output string than overflow buffer */
- /* it would be still better to either return a status */
- /* or ensure the target buffer is large enough and it never happen */
- break;
- }
- }
- pout[-1] = 0;
- }
-
-
- // BearSSL doesn't define a true insecure decoder, so we make one ourselves
- // from the simple parser. It generates the issuer and subject hashes and
- // the SHA1 fingerprint, only one (or none!) of which will be used to
- // "verify" the certificate.
-
- // Private x509 decoder state
- struct br_x509_pubkeyfingerprint_context {
- const br_x509_class *vtable;
- bool done_cert; // did we parse the first cert already?
- bool fingerprint_all;
- uint8_t *pubkey_recv_fingerprint;
- const uint8_t *fingerprint1;
- const uint8_t *fingerprint2;
- unsigned usages; // pubkey usage
- br_x509_decoder_context ctx; // defined in BearSSL
- };
-
- // Callback on the first byte of any certificate
- static void pubkeyfingerprint_start_chain(const br_x509_class **ctx, const char *server_name) {
- br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
- // Don't process anything but the first certificate in the chain
- if (!xc->done_cert) {
- br_x509_decoder_init(&xc->ctx, nullptr, nullptr, nullptr, nullptr);
- }
- (void)server_name; // ignore server name
- }
-
- // Callback for each certificate present in the chain (but only operates
- // on the first one by design).
- static void pubkeyfingerprint_start_cert(const br_x509_class **ctx, uint32_t length) {
- (void) ctx; // do nothing
- (void) length;
- }
-
- // Callback for each byte stream in the chain. Only process first cert.
- static void pubkeyfingerprint_append(const br_x509_class **ctx, const unsigned char *buf, size_t len) {
- br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
- // Don't process anything but the first certificate in the chain
- if (!xc->done_cert) {
- br_x509_decoder_push(&xc->ctx, (const void*)buf, len);
- }
- }
-
- // Callback on individual cert end.
- static void pubkeyfingerprint_end_cert(const br_x509_class **ctx) {
- br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
- xc->done_cert = true; // first cert already processed
- }
-
- static void pubkeyfingerprint_pubkey_fingerprint(br_sha1_context *shactx, br_rsa_public_key rsakey) {
- br_sha1_init(shactx);
- br_sha1_update(shactx, "ssh-rsa", 7); // tag
- br_sha1_update(shactx, rsakey.e, rsakey.elen); // exponent
- br_sha1_update(shactx, rsakey.n, rsakey.nlen); // modulus
- }
-
- // Callback when complete chain has been parsed.
- // Return 0 on validation success, !0 on validation error
- static unsigned pubkeyfingerprint_end_chain(const br_x509_class **ctx) {
- br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
-
- br_sha1_context sha1_context;
- pubkeyfingerprint_pubkey_fingerprint(&sha1_context, xc->ctx.pkey.key.rsa);
- br_sha1_out(&sha1_context, xc->pubkey_recv_fingerprint); // copy to fingerprint
-
- if (!xc->fingerprint_all) {
- if (0 == memcmp(xc->fingerprint1, xc->pubkey_recv_fingerprint, 20)) {
- return 0;
- }
- if (0 == memcmp(xc->fingerprint2, xc->pubkey_recv_fingerprint, 20)) {
- return 0;
- }
- return 1; // no match, error
- } else {
- // Default (no validation at all) or no errors in prior checks = success.
- return 0;
- }
- }
-
- // Return the public key from the validator (set by x509_minimal)
- static const br_x509_pkey *pubkeyfingerprint_get_pkey(const br_x509_class *const *ctx, unsigned *usages) {
- const br_x509_pubkeyfingerprint_context *xc = (const br_x509_pubkeyfingerprint_context *)ctx;
-
- if (usages != NULL) {
- *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN; // I said we were insecure!
- }
- return &xc->ctx.pkey;
- }
-
- // Set up the x509 insecure data structures for BearSSL core to use.
- void br_x509_pubkeyfingerprint_init(br_x509_pubkeyfingerprint_context *ctx,
- const uint8_t *fingerprint1, const uint8_t *fingerprint2,
- uint8_t *recv_fingerprint,
- bool fingerprint_all) {
- static const br_x509_class br_x509_pubkeyfingerprint_vtable PROGMEM = {
- sizeof(br_x509_pubkeyfingerprint_context),
- pubkeyfingerprint_start_chain,
- pubkeyfingerprint_start_cert,
- pubkeyfingerprint_append,
- pubkeyfingerprint_end_cert,
- pubkeyfingerprint_end_chain,
- pubkeyfingerprint_get_pkey
- };
-
- memset(ctx, 0, sizeof * ctx);
- ctx->vtable = &br_x509_pubkeyfingerprint_vtable;
- ctx->done_cert = false;
- ctx->fingerprint1 = fingerprint1;
- ctx->fingerprint2 = fingerprint2;
- ctx->pubkey_recv_fingerprint = recv_fingerprint;
- ctx->fingerprint_all = fingerprint_all;
- }
-
- // We limit to a single cipher to reduce footprint
- // we reference it, don't put in PROGMEM
- static const uint16_t suites[] = {
-#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_TLS_FORCE_EC_CIPHER)
- BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
-#else
- BR_TLS_RSA_WITH_AES_128_GCM_SHA256
-#endif
- };
-
- // Default initializion for our SSL clients
- static void br_ssl_client_base_init(br_ssl_client_context *cc) {
- br_ssl_client_zero(cc);
- // forbid SSL renegociation, as we free the Private Key after handshake
- br_ssl_engine_add_flags(&cc->eng, BR_OPT_NO_RENEGOTIATION);
-
- br_ssl_engine_set_versions(&cc->eng, BR_TLS12, BR_TLS12);
- br_ssl_engine_set_suites(&cc->eng, suites, (sizeof suites) / (sizeof suites[0]));
- br_ssl_client_set_default_rsapub(cc);
- br_ssl_engine_set_default_rsavrfy(&cc->eng);
-
- // install hashes
- br_ssl_engine_set_hash(&cc->eng, br_sha256_ID, &br_sha256_vtable);
- br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf);
-
- // AES CTR/GCM small version, not contstant time (we don't really care here as there is no TPM anyways)
- br_ssl_engine_set_gcm(&cc->eng, &br_sslrec_in_gcm_vtable, &br_sslrec_out_gcm_vtable);
- br_ssl_engine_set_aes_ctr(&cc->eng, &br_aes_small_ctr_vtable);
- br_ssl_engine_set_ghash(&cc->eng, &br_ghash_ctmul32);
-
-#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_TLS_FORCE_EC_CIPHER)
- // we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt unless forced
- br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15);
-#endif
- }
-}
-
-// Called by connect() to do the actual SSL setup and handshake.
-// Returns if the SSL handshake succeeded.
-bool WiFiClientSecure_light::_connectSSL(const char* hostName) {
-#ifdef USE_MQTT_AWS_IOT
- if ((!_chain_P) || (!_sk_ec_P)) {
- setLastError(ERR_MISSING_EC_KEY);
- return false;
- }
-#endif
-
- // Validation context, either full CA validation or checking only fingerprints
-#ifdef USE_MQTT_TLS_CA_CERT
- br_x509_minimal_context *x509_minimal;
-#else
- br_x509_pubkeyfingerprint_context *x509_insecure;
-#endif
-
- LOG_HEAP_SIZE("_connectSSL.start");
-
- do { // used to exit on Out of Memory error and keep all cleanup code at the same place
- // ============================================================
- // allocate Thunk stack, move to alternate stack and initialize
- stack_thunk_light_add_ref();
- LOG_HEAP_SIZE("Thunk allocated");
- DEBUG_BSSL("_connectSSL: start connection\n");
- _freeSSL();
- clearLastError();
- if (!stack_thunk_light_get_stack_bot()) break;
-
- _ctx_present = true;
- _eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr
-
- br_ssl_client_base_init(_sc.get());
-
- // ============================================================
- // Allocatte and initialize Decoder Context
- LOG_HEAP_SIZE("_connectSSL before DecoderContext allocation");
- // Only failure possible in the installation is OOM
- #ifdef USE_MQTT_TLS_CA_CERT
- x509_minimal = (br_x509_minimal_context*) malloc(sizeof(br_x509_minimal_context));
- if (!x509_minimal) break;
- br_x509_minimal_init(x509_minimal, &br_sha256_vtable, _ta_P, 1);
- br_x509_minimal_set_rsa(x509_minimal, br_ssl_engine_get_rsavrfy(_eng));
- br_x509_minimal_set_hash(x509_minimal, br_sha256_ID, &br_sha256_vtable);
- br_ssl_engine_set_x509(_eng, &x509_minimal->vtable);
-
- #else
- x509_insecure = (br_x509_pubkeyfingerprint_context*) malloc(sizeof(br_x509_pubkeyfingerprint_context));
- //x509_insecure = std::unique_ptr(new br_x509_pubkeyfingerprint_context);
- if (!x509_insecure) break;
- br_x509_pubkeyfingerprint_init(x509_insecure, _fingerprint1, _fingerprint2, _recv_fingerprint, _fingerprint_any);
- br_ssl_engine_set_x509(_eng, &x509_insecure->vtable);
- #endif
- LOG_HEAP_SIZE("_connectSSL after DecoderContext allocation");
-
- // ============================================================
- // Set send/receive buffers
- br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size);
-
- // ============================================================
- // allocate Private key if needed, only if USE_MQTT_AWS_IOT
- LOG_HEAP_SIZE("_connectSSL before PrivKey allocation");
- #ifdef USE_MQTT_AWS_IOT
- // ============================================================
- // Set the EC Private Key, only USE_MQTT_AWS_IOT
- // limited to P256 curve
- br_ssl_client_set_single_ec(_sc.get(), _chain_P, 1,
- _sk_ec_P, _allowed_usages,
- _cert_issuer_key_type, &br_ec_p256_m15, br_ecdsa_sign_asn1_get_default());
- #endif // USE_MQTT_AWS_IOT
-
- // ============================================================
- // Start TLS connection, ALL
- if (!br_ssl_client_reset(_sc.get(), hostName, 0)) break;
-
- auto ret = _wait_for_handshake();
- #ifdef DEBUG_ESP_SSL
- if (!ret) {
- DEBUG_BSSL("Couldn't connect. Error = %d\n", getLastError());
- } else {
- DEBUG_BSSL("Connected! MFLNStatus = %d\n", getMFLNStatus());
- }
- #endif
- LOG_HEAP_SIZE("_connectSSL.end");
- _max_thunkstack_use = stack_thunk_light_get_max_usage();
- stack_thunk_light_del_ref();
- //stack_thunk_light_repaint();
- LOG_HEAP_SIZE("_connectSSL.end, freeing StackThunk");
-
- #ifdef USE_MQTT_TLS_CA_CERT
- free(x509_minimal);
- #else
- free(x509_insecure);
- #endif
- LOG_HEAP_SIZE("_connectSSL after release of Priv Key");
- return ret;
- } while (0);
-
- // ============================================================
- // if we arrived here, this means we had an OOM error, cleaning up
- setLastError(ERR_OOM);
- DEBUG_BSSL("_connectSSL: Out of memory\n");
- stack_thunk_light_del_ref();
-#ifdef USE_MQTT_TLS_CA_CERT
- free(x509_minimal);
-#else
- free(x509_insecure);
-#endif
- LOG_HEAP_SIZE("_connectSSL clean_on_error");
- return false;
-}
-
-};
-
-#include "t_bearssl_tasmota_config.h"
-
-#endif // USE_MQTT_TLS
+/*
+ WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries
+ - Mostly compatible with Arduino WiFi shield library and standard
+ WiFiClient/ServerSecure (except for certificate handling).
+
+ Copyright (c) 2018 Earle F. Philhower, III
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "my_user_config.h"
+#if defined(ESP8266) && defined(USE_TLS)
+
+// #define DEBUG_TLS
+// #define DEBUG_ESP_SSL
+
+#define LWIP_INTERNAL
+
+#include
+#include
+#include
+
+extern "C" {
+#include "osapi.h"
+#include "ets_sys.h"
+}
+#include "debug.h"
+#include "WiFiClientSecureLightBearSSL.h" // needs to be before "ESP8266WiFi.h" to avoid conflict with Arduino headers
+#include "ESP8266WiFi.h"
+#include "WiFiClient.h"
+#include "StackThunk_light.h"
+#include "lwip/opt.h"
+#include "lwip/ip.h"
+#include "lwip/tcp.h"
+#include "lwip/inet.h"
+#include "lwip/netif.h"
+#include
+#include "c_types.h"
+
+#include
+#ifndef ARDUINO_ESP8266_RELEASE_2_5_2
+#undef DEBUG_TLS
+#endif
+
+#ifdef DEBUG_TLS
+#include "coredecls.h"
+#define LOG_HEAP_SIZE(a) _Log_heap_size(a)
+void _Log_heap_size(const char *msg) {
+ register uint32_t *sp asm("a1");
+ int freestack = 4 * (sp - g_pcont->stack);
+ Serial.printf("%s %d, Fragmentation=%d, Thunkstack=%d, Free stack=%d, FreeContStack=%d\n",
+ msg, ESP.getFreeHeap(), ESP.getHeapFragmentation(), stack_thunk_light_get_max_usage(),
+ freestack, ESP.getFreeContStack());
+}
+#else
+#define LOG_HEAP_SIZE(a)
+#endif
+
+// Stack thunked versions of calls
+// Initially in BearSSLHelpers.h
+extern "C" {
+extern unsigned char *thunk_light_br_ssl_engine_recvapp_buf( const br_ssl_engine_context *cc, size_t *len);
+extern void thunk_light_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len);
+extern unsigned char *thunk_light_br_ssl_engine_recvrec_buf( const br_ssl_engine_context *cc, size_t *len);
+extern void thunk_light_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len);
+extern unsigned char *thunk_light_br_ssl_engine_sendapp_buf( const br_ssl_engine_context *cc, size_t *len);
+extern void thunk_light_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len);
+extern unsigned char *thunk_light_br_ssl_engine_sendrec_buf( const br_ssl_engine_context *cc, size_t *len);
+extern void thunk_light_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len);
+};
+
+// Second stack thunked helpers
+make_stack_thunk_light(br_ssl_engine_recvapp_ack);
+make_stack_thunk_light(br_ssl_engine_recvapp_buf);
+make_stack_thunk_light(br_ssl_engine_recvrec_ack);
+make_stack_thunk_light(br_ssl_engine_recvrec_buf);
+make_stack_thunk_light(br_ssl_engine_sendapp_ack);
+make_stack_thunk_light(br_ssl_engine_sendapp_buf);
+make_stack_thunk_light(br_ssl_engine_sendrec_ack);
+make_stack_thunk_light(br_ssl_engine_sendrec_buf);
+
+// create new version of Thunk function to store on SYS stack
+// unless the Thunk was initialized. Thanks to AES128 GCM, we can keep
+// symetric processing on the stack
+void min_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len) {
+ if (stack_thunk_light_get_refcnt()) {
+ return thunk_light_br_ssl_engine_recvapp_ack(cc, len);
+ } else {
+ return br_ssl_engine_recvapp_ack(cc, len);
+ }
+}
+unsigned char *min_br_ssl_engine_recvapp_buf(const br_ssl_engine_context *cc, size_t *len) {
+ if (stack_thunk_light_get_refcnt()) {
+ return thunk_light_br_ssl_engine_recvapp_buf(cc, len);
+ } else {
+ return br_ssl_engine_recvapp_buf(cc, len);
+ }
+}
+void min_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len) {
+ if (stack_thunk_light_get_refcnt()) {
+ return thunk_light_br_ssl_engine_recvrec_ack(cc, len);
+ } else {
+ return br_ssl_engine_recvrec_ack(cc, len);
+ }
+}
+unsigned char *min_br_ssl_engine_recvrec_buf(const br_ssl_engine_context *cc, size_t *len) {
+ if (stack_thunk_light_get_refcnt()) {
+ return thunk_light_br_ssl_engine_recvrec_buf(cc, len);
+ } else {
+ return br_ssl_engine_recvrec_buf(cc, len);
+ }
+}
+void min_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len) {
+ if (stack_thunk_light_get_refcnt()) {
+ return thunk_light_br_ssl_engine_sendapp_ack(cc, len);
+ } else {
+ return br_ssl_engine_sendapp_ack(cc, len);
+ }
+}
+unsigned char *min_br_ssl_engine_sendapp_buf(const br_ssl_engine_context *cc, size_t *len) {
+ if (stack_thunk_light_get_refcnt()) {
+ return thunk_light_br_ssl_engine_sendapp_buf(cc, len);
+ } else {
+ return br_ssl_engine_sendapp_buf(cc, len);
+ }
+}
+void min_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len) {
+ if (stack_thunk_light_get_refcnt()) {
+ return thunk_light_br_ssl_engine_sendrec_ack(cc, len);
+ } else {
+ return br_ssl_engine_sendrec_ack(cc, len);
+ }
+}
+unsigned char *min_br_ssl_engine_sendrec_buf(const br_ssl_engine_context *cc, size_t *len) {
+ if (stack_thunk_light_get_refcnt()) {
+ return thunk_light_br_ssl_engine_sendrec_buf(cc, len);
+ } else {
+ return br_ssl_engine_sendrec_buf(cc, len);
+ }
+}
+
+// Use min_ instead of original thunk_
+#define br_ssl_engine_recvapp_ack min_br_ssl_engine_recvapp_ack
+#define br_ssl_engine_recvapp_buf min_br_ssl_engine_recvapp_buf
+#define br_ssl_engine_recvrec_ack min_br_ssl_engine_recvrec_ack
+#define br_ssl_engine_recvrec_buf min_br_ssl_engine_recvrec_buf
+#define br_ssl_engine_sendapp_ack min_br_ssl_engine_sendapp_ack
+#define br_ssl_engine_sendapp_buf min_br_ssl_engine_sendapp_buf
+#define br_ssl_engine_sendrec_ack min_br_ssl_engine_sendrec_ack
+#define br_ssl_engine_sendrec_buf min_br_ssl_engine_sendrec_buf
+
+//#define DEBUG_ESP_SSL
+#ifdef DEBUG_ESP_SSL
+//#define DEBUG_BSSL(fmt, ...) DEBUG_ESP_PORT.printf_P((PGM_P)PSTR( "BSSL:" fmt), ## __VA_ARGS__)
+#define DEBUG_BSSL(fmt, ...) Serial.printf(fmt, ## __VA_ARGS__)
+#else
+#define DEBUG_BSSL(...)
+#endif
+
+namespace BearSSL {
+
+void WiFiClientSecure_light::_clear() {
+ // TLS handshake may take more than the 5 second default timeout
+ _timeout = 10000; // 10 seconds max, it should never go over 6 seconds
+
+ _sc = nullptr;
+ _ctx_present = false;
+ _eng = nullptr;
+ _iobuf_in = nullptr;
+ _iobuf_out = nullptr;
+ _now = 0; // You can override or ensure time() is correct w/configTime
+ setBufferSizes(1024, 1024); // reasonable minimum
+ _handshake_done = false;
+ _last_error = 0;
+ _recvapp_buf = nullptr;
+ _recvapp_len = 0;
+ _fingerprint_any = true; // by default accept all fingerprints
+ _fingerprint1 = nullptr;
+ _fingerprint2 = nullptr;
+ _chain_P = nullptr;
+ _sk_ec_P = nullptr;
+ _ta_P = nullptr;
+ _max_thunkstack_use = 0;
+}
+
+// Constructor
+WiFiClientSecure_light::WiFiClientSecure_light(int recv, int xmit) : WiFiClient() {
+ _clear();
+LOG_HEAP_SIZE("StackThunk before");
+ //stack_thunk_light_add_ref();
+LOG_HEAP_SIZE("StackThunk after");
+ // now finish the setup
+ setBufferSizes(recv, xmit); // reasonable minimum
+ allocateBuffers();
+}
+
+WiFiClientSecure_light::~WiFiClientSecure_light() {
+ if (_client) {
+ _client->unref();
+ _client = nullptr;
+ }
+ //_cipher_list = nullptr; // std::shared will free if last reference
+ _freeSSL();
+}
+
+void WiFiClientSecure_light::allocateBuffers(void) {
+ // We prefer to allocate all buffers at start, rather than lazy allocation and deallocation
+ // in the long run it avoids heap fragmentation and improves stability
+ LOG_HEAP_SIZE("allocateBuffers before");
+ _sc = std::make_shared();
+ LOG_HEAP_SIZE("allocateBuffers ClientContext");
+ _iobuf_in = std::shared_ptr(new unsigned char[_iobuf_in_size], std::default_delete());
+ _iobuf_out = std::shared_ptr(new unsigned char[_iobuf_out_size], std::default_delete());
+ LOG_HEAP_SIZE("allocateBuffers after");
+}
+
+void WiFiClientSecure_light::setClientECCert(const br_x509_certificate *cert, const br_ec_private_key *sk,
+ unsigned allowed_usages, unsigned cert_issuer_key_type) {
+ _chain_P = cert;
+ _sk_ec_P = sk;
+ _allowed_usages = allowed_usages;
+ _cert_issuer_key_type = cert_issuer_key_type;
+}
+
+void WiFiClientSecure_light::setTrustAnchor(const br_x509_trust_anchor *ta) {
+ _ta_P = ta;
+}
+
+void WiFiClientSecure_light::setBufferSizes(int recv, int xmit) {
+ // Following constants taken from bearssl/src/ssl/ssl_engine.c (not exported unfortunately)
+ const int MAX_OUT_OVERHEAD = 85;
+ const int MAX_IN_OVERHEAD = 325;
+
+ // The data buffers must be between 512B and 16KB
+ recv = std::max(512, std::min(16384, recv));
+ xmit = std::max(512, std::min(16384, xmit));
+
+ // Add in overhead for SSL protocol
+ recv += MAX_IN_OVERHEAD;
+ xmit += MAX_OUT_OVERHEAD;
+ _iobuf_in_size = recv;
+ _iobuf_out_size = xmit;
+}
+
+bool WiFiClientSecure_light::stop(unsigned int maxWaitMs) {
+#ifdef ARDUINO_ESP8266_RELEASE_2_4_2
+ WiFiClient::stop(); // calls our virtual flush()
+ _freeSSL();
+ return true;
+#else
+ bool ret = WiFiClient::stop(maxWaitMs); // calls our virtual flush()
+ _freeSSL();
+ return ret;
+#endif
+}
+
+bool WiFiClientSecure_light::flush(unsigned int maxWaitMs) {
+ (void) _run_until(BR_SSL_SENDAPP);
+#ifdef ARDUINO_ESP8266_RELEASE_2_4_2
+ WiFiClient::flush();
+#else
+ return WiFiClient::flush(maxWaitMs);
+#endif
+}
+
+int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port) {
+ DEBUG_BSSL("connect(%s,%d)", ip.toString().c_str(), port);
+ clearLastError();
+ if (!WiFiClient::connect(ip, port)) {
+ setLastError(ERR_TCP_CONNECT);
+ return 0;
+ }
+ return _connectSSL(nullptr);
+}
+
+int WiFiClientSecure_light::connect(const char* name, uint16_t port) {
+ DEBUG_BSSL("connect(%s,%d)\n", name, port);
+ IPAddress remote_addr;
+ clearLastError();
+ if (!WiFi.hostByName(name, remote_addr)) {
+ DEBUG_BSSL("connect: Name loopup failure\n");
+ setLastError(ERR_CANT_RESOLVE_IP);
+ return 0;
+ }
+ DEBUG_BSSL("connect(%s,%d)\n", remote_addr.toString().c_str(), port);
+ if (!WiFiClient::connect(remote_addr, port)) {
+ DEBUG_BSSL("connect: Unable to connect TCP socket\n");
+ _last_error = ERR_TCP_CONNECT;
+ return 0;
+ }
+ LOG_HEAP_SIZE("Before calling _connectSSL");
+ return _connectSSL(name);
+}
+
+void WiFiClientSecure_light::_freeSSL() {
+ _ctx_present = false;
+ _recvapp_buf = nullptr;
+ _recvapp_len = 0;
+ // This connection is toast
+ _handshake_done = false;
+}
+
+bool WiFiClientSecure_light::_clientConnected() {
+ return (_client && _client->state() == ESTABLISHED);
+}
+
+uint8_t WiFiClientSecure_light::connected() {
+ if (available() || (_clientConnected() && _handshake_done)) {
+ return true;
+ }
+ return false;
+}
+
+size_t WiFiClientSecure_light::_write(const uint8_t *buf, size_t size, bool pmem) {
+ size_t sent_bytes = 0;
+
+ if (!connected() || !size || !_handshake_done) {
+ return 0;
+ }
+
+ do {
+ // Ensure we yield if we need multiple fragments to avoid WDT
+ if (sent_bytes) {
+ optimistic_yield(1000);
+ }
+
+ // Get BearSSL to a state where we can send
+ if (_run_until(BR_SSL_SENDAPP) < 0) {
+ break;
+ }
+
+ if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) {
+ size_t sendapp_len;
+ unsigned char *sendapp_buf = br_ssl_engine_sendapp_buf(_eng, &sendapp_len);
+ int to_send = size > sendapp_len ? sendapp_len : size;
+ if (pmem) {
+ memcpy_P(sendapp_buf, buf, to_send);
+ } else {
+ memcpy(sendapp_buf, buf, to_send);
+ }
+ br_ssl_engine_sendapp_ack(_eng, to_send);
+ br_ssl_engine_flush(_eng, 0);
+ flush();
+ buf += to_send;
+ sent_bytes += to_send;
+ size -= to_send;
+ } else {
+ break;
+ }
+ } while (size);
+
+ LOG_HEAP_SIZE("_write");
+ return sent_bytes;
+}
+
+size_t WiFiClientSecure_light::write(const uint8_t *buf, size_t size) {
+ return _write(buf, size, false);
+}
+
+size_t WiFiClientSecure_light::write_P(PGM_P buf, size_t size) {
+ return _write((const uint8_t *)buf, size, true);
+}
+
+// We have to manually read and send individual chunks.
+size_t WiFiClientSecure_light::write(Stream& stream) {
+ size_t totalSent = 0;
+ size_t countRead;
+ size_t countSent;
+
+ if (!connected() || !_handshake_done) {
+ DEBUG_BSSL("write: Connect/handshake not completed yet\n");
+ return 0;
+ }
+
+ do {
+ uint8_t temp[256]; // Temporary chunk size same as ClientContext
+ countSent = 0;
+ countRead = stream.readBytes(temp, sizeof(temp));
+ if (countRead) {
+ countSent = _write((const uint8_t*)temp, countRead, true);
+ totalSent += countSent;
+ }
+ yield(); // Feed the WDT
+ } while ((countSent == countRead) && (countSent > 0));
+ return totalSent;
+}
+
+int WiFiClientSecure_light::read(uint8_t *buf, size_t size) {
+ if (!ctx_present() || !_handshake_done) {
+ return -1;
+ }
+
+ int avail = available();
+ bool conn = connected();
+ if (!avail && conn) {
+ return 0; // We're still connected, but nothing to read
+ }
+ if (!avail && !conn) {
+ DEBUG_BSSL("read: Not connected, none left available\n");
+ return -1;
+ }
+
+ if (avail) {
+ // Take data from the recvapp buffer
+ int to_copy = _recvapp_len < size ? _recvapp_len : size;
+ memcpy(buf, _recvapp_buf, to_copy);
+ br_ssl_engine_recvapp_ack(_eng, to_copy);
+ _recvapp_buf = nullptr;
+ _recvapp_len = 0;
+ return to_copy;
+ }
+
+ if (!conn) {
+ DEBUG_BSSL("read: Not connected\n");
+ return -1;
+ }
+ return 0; // If we're connected, no error but no read.
+}
+
+int WiFiClientSecure_light::read() {
+ uint8_t c;
+ if (1 == read(&c, 1)) {
+ return c;
+ }
+ DEBUG_BSSL("read: failed\n");
+ return -1;
+}
+
+int WiFiClientSecure_light::available() {
+ if (_recvapp_buf) {
+ return _recvapp_len; // Anything from last call?
+ }
+ _recvapp_buf = nullptr;
+ _recvapp_len = 0;
+ if (!ctx_present() || _run_until(BR_SSL_RECVAPP, false) < 0) {
+ return 0;
+ }
+ int st = br_ssl_engine_current_state(_eng);
+ if (st == BR_SSL_CLOSED) {
+ return 0; // Nothing leftover, SSL is closed
+ }
+ if (st & BR_SSL_RECVAPP) {
+ _recvapp_buf = br_ssl_engine_recvapp_buf(_eng, &_recvapp_len);
+ return _recvapp_len;
+ }
+
+ return 0;
+}
+
+int WiFiClientSecure_light::peek() {
+ if (!ctx_present() || !available()) {
+ DEBUG_BSSL("peek: Not connected, none left available\n");
+ return -1;
+ }
+ if (_recvapp_buf && _recvapp_len) {
+ return _recvapp_buf[0];
+ }
+ DEBUG_BSSL("peek: No data left\n");
+ return -1;
+}
+
+size_t WiFiClientSecure_light::peekBytes(uint8_t *buffer, size_t length) {
+ size_t to_copy = 0;
+ if (!ctx_present()) {
+ DEBUG_BSSL("peekBytes: Not connected\n");
+ return 0;
+ }
+
+ _startMillis = millis();
+ while ((available() < (int) length) && ((millis() - _startMillis) < 5000)) {
+ yield();
+ }
+
+ to_copy = _recvapp_len < length ? _recvapp_len : length;
+ memcpy(buffer, _recvapp_buf, to_copy);
+ return to_copy;
+}
+
+/* --- Copied almost verbatim from BEARSSL SSL_IO.C ---
+ Run the engine, until the specified target state is achieved, or
+ an error occurs. The target state is SENDAPP, RECVAPP, or the
+ combination of both (the combination matches either). When a match is
+ achieved, this function returns 0. On error, it returns -1.
+*/
+int WiFiClientSecure_light::_run_until(unsigned target, bool blocking) {
+//LOG_HEAP_SIZE("_run_until 1");
+ if (!ctx_present()) {
+ DEBUG_BSSL("_run_until: Not connected\n");
+ return -1;
+ }
+ for (int no_work = 0; blocking || no_work < 2;) {
+ if (blocking) {
+ // Only for blocking operations can we afford to yield()
+ optimistic_yield(100);
+ }
+
+ int state;
+ state = br_ssl_engine_current_state(_eng);
+ if (state & BR_SSL_CLOSED) {
+ return -1;
+ }
+
+ if (!(_client->state() == ESTABLISHED) && !WiFiClient::available()) {
+ return (state & target) ? 0 : -1;
+ }
+
+ /*
+ If there is some record data to send, do it. This takes
+ precedence over everything else.
+ */
+ if (state & BR_SSL_SENDREC) {
+ unsigned char *buf;
+ size_t len;
+ int wlen;
+
+ buf = br_ssl_engine_sendrec_buf(_eng, &len);
+ wlen = WiFiClient::write(buf, len);
+ if (wlen <= 0) {
+ /*
+ If we received a close_notify and we
+ still send something, then we have our
+ own response close_notify to send, and
+ the peer is allowed by RFC 5246 not to
+ wait for it.
+ */
+ return -1;
+ }
+ if (wlen > 0) {
+ br_ssl_engine_sendrec_ack(_eng, wlen);
+ }
+ no_work = 0;
+ continue;
+ }
+
+ /*
+ If we reached our target, then we are finished.
+ */
+ if (state & target) {
+ return 0;
+ }
+ /*
+ If some application data must be read, and we did not
+ exit, then this means that we are trying to write data,
+ and that's not possible until the application data is
+ read. This may happen if using a shared in/out buffer,
+ and the underlying protocol is not strictly half-duplex.
+ This is unrecoverable here, so we report an error.
+ */
+ if (state & BR_SSL_RECVAPP) {
+ DEBUG_BSSL("_run_until: Fatal protocol state\n");
+ return -1;
+ }
+ /*
+ If we reached that point, then either we are trying
+ to read data and there is some, or the engine is stuck
+ until a new record is obtained.
+ */
+ if (state & BR_SSL_RECVREC) {
+ if (WiFiClient::available()) {
+ unsigned char *buf;
+ size_t len;
+ int rlen;
+
+ buf = br_ssl_engine_recvrec_buf(_eng, &len);
+ rlen = WiFiClient::read(buf, len);
+ if (rlen < 0) {
+ return -1;
+ }
+ if (rlen > 0) {
+ br_ssl_engine_recvrec_ack(_eng, rlen);
+ }
+ no_work = 0;
+ continue;
+ }
+ }
+ /*
+ We can reach that point if the target RECVAPP, and
+ the state contains SENDAPP only. This may happen with
+ a shared in/out buffer. In that case, we must flush
+ the buffered data to "make room" for a new incoming
+ record.
+ */
+ br_ssl_engine_flush(_eng, 0);
+
+ no_work++; // We didn't actually advance here
+ }
+ // We only get here if we ran through the loop without getting anything done
+ return -1;
+}
+
+bool WiFiClientSecure_light::_wait_for_handshake() {
+ _handshake_done = false;
+ while (!_handshake_done && _clientConnected()) {
+ int ret = _run_until(BR_SSL_SENDAPP);
+ if (ret < 0) {
+ DEBUG_BSSL("_wait_for_handshake: failed\n");
+ break;
+ }
+ if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) {
+ _handshake_done = true;
+ }
+ optimistic_yield(1000);
+ }
+ return _handshake_done;
+}
+
+static uint8_t htoi (unsigned char c)
+{
+ if (c>='0' && c <='9') return c - '0';
+ else if (c>='A' && c<='F') return 10 + c - 'A';
+ else if (c>='a' && c<='f') return 10 + c - 'a';
+ else return 255;
+}
+
+extern "C" {
+
+ // see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c
+ void tohex(unsigned char * in, size_t insz, char * out, size_t outsz) {
+ unsigned char * pin = in;
+ static const char * hex = "0123456789ABCDEF";
+ char * pout = out;
+ for(; pin < in+insz; pout +=3, pin++){
+ pout[0] = hex[(*pin>>4) & 0xF];
+ pout[1] = hex[ *pin & 0xF];
+ pout[2] = ':';
+ if (pout + 3 - out > outsz){
+ /* Better to truncate output string than overflow buffer */
+ /* it would be still better to either return a status */
+ /* or ensure the target buffer is large enough and it never happen */
+ break;
+ }
+ }
+ pout[-1] = 0;
+ }
+
+
+ // BearSSL doesn't define a true insecure decoder, so we make one ourselves
+ // from the simple parser. It generates the issuer and subject hashes and
+ // the SHA1 fingerprint, only one (or none!) of which will be used to
+ // "verify" the certificate.
+
+ // Private x509 decoder state
+ struct br_x509_pubkeyfingerprint_context {
+ const br_x509_class *vtable;
+ bool done_cert; // did we parse the first cert already?
+ bool fingerprint_all;
+ uint8_t *pubkey_recv_fingerprint;
+ const uint8_t *fingerprint1;
+ const uint8_t *fingerprint2;
+ unsigned usages; // pubkey usage
+ br_x509_decoder_context ctx; // defined in BearSSL
+ };
+
+ // Callback on the first byte of any certificate
+ static void pubkeyfingerprint_start_chain(const br_x509_class **ctx, const char *server_name) {
+ br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
+ // Don't process anything but the first certificate in the chain
+ if (!xc->done_cert) {
+ br_x509_decoder_init(&xc->ctx, nullptr, nullptr, nullptr, nullptr);
+ }
+ (void)server_name; // ignore server name
+ }
+
+ // Callback for each certificate present in the chain (but only operates
+ // on the first one by design).
+ static void pubkeyfingerprint_start_cert(const br_x509_class **ctx, uint32_t length) {
+ (void) ctx; // do nothing
+ (void) length;
+ }
+
+ // Callback for each byte stream in the chain. Only process first cert.
+ static void pubkeyfingerprint_append(const br_x509_class **ctx, const unsigned char *buf, size_t len) {
+ br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
+ // Don't process anything but the first certificate in the chain
+ if (!xc->done_cert) {
+ br_x509_decoder_push(&xc->ctx, (const void*)buf, len);
+ }
+ }
+
+ // Callback on individual cert end.
+ static void pubkeyfingerprint_end_cert(const br_x509_class **ctx) {
+ br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
+ xc->done_cert = true; // first cert already processed
+ }
+
+ static void pubkeyfingerprint_pubkey_fingerprint(br_sha1_context *shactx, br_rsa_public_key rsakey) {
+ br_sha1_init(shactx);
+ br_sha1_update(shactx, "ssh-rsa", 7); // tag
+ br_sha1_update(shactx, rsakey.e, rsakey.elen); // exponent
+ br_sha1_update(shactx, rsakey.n, rsakey.nlen); // modulus
+ }
+
+ // Callback when complete chain has been parsed.
+ // Return 0 on validation success, !0 on validation error
+ static unsigned pubkeyfingerprint_end_chain(const br_x509_class **ctx) {
+ br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
+
+ br_sha1_context sha1_context;
+ pubkeyfingerprint_pubkey_fingerprint(&sha1_context, xc->ctx.pkey.key.rsa);
+ br_sha1_out(&sha1_context, xc->pubkey_recv_fingerprint); // copy to fingerprint
+
+ if (!xc->fingerprint_all) {
+ if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint1, 20)) {
+ return 0;
+ }
+ if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint2, 20)) {
+ return 0;
+ }
+ return 1; // no match, error
+ } else {
+ // Default (no validation at all) or no errors in prior checks = success.
+ return 0;
+ }
+ }
+
+ // Return the public key from the validator (set by x509_minimal)
+ static const br_x509_pkey *pubkeyfingerprint_get_pkey(const br_x509_class *const *ctx, unsigned *usages) {
+ const br_x509_pubkeyfingerprint_context *xc = (const br_x509_pubkeyfingerprint_context *)ctx;
+
+ if (usages != NULL) {
+ *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN; // I said we were insecure!
+ }
+ return &xc->ctx.pkey;
+ }
+
+ // Set up the x509 insecure data structures for BearSSL core to use.
+ void br_x509_pubkeyfingerprint_init(br_x509_pubkeyfingerprint_context *ctx,
+ const uint8_t *fingerprint1, const uint8_t *fingerprint2,
+ uint8_t *recv_fingerprint,
+ bool fingerprint_all) {
+ static const br_x509_class br_x509_pubkeyfingerprint_vtable PROGMEM = {
+ sizeof(br_x509_pubkeyfingerprint_context),
+ pubkeyfingerprint_start_chain,
+ pubkeyfingerprint_start_cert,
+ pubkeyfingerprint_append,
+ pubkeyfingerprint_end_cert,
+ pubkeyfingerprint_end_chain,
+ pubkeyfingerprint_get_pkey
+ };
+
+ memset(ctx, 0, sizeof * ctx);
+ ctx->vtable = &br_x509_pubkeyfingerprint_vtable;
+ ctx->done_cert = false;
+ ctx->fingerprint1 = fingerprint1;
+ ctx->fingerprint2 = fingerprint2;
+ ctx->pubkey_recv_fingerprint = recv_fingerprint;
+ ctx->fingerprint_all = fingerprint_all;
+ }
+
+ // We limit to a single cipher to reduce footprint
+ // we reference it, don't put in PROGMEM
+ static const uint16_t suites[] = {
+#ifdef USE_MQTT_TLS_FORCE_EC_CIPHER
+ BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+#else
+ BR_TLS_RSA_WITH_AES_128_GCM_SHA256
+#endif
+ };
+
+ // Default initializion for our SSL clients
+ static void br_ssl_client_base_init(br_ssl_client_context *cc) {
+ br_ssl_client_zero(cc);
+ // forbid SSL renegociation, as we free the Private Key after handshake
+ br_ssl_engine_add_flags(&cc->eng, BR_OPT_NO_RENEGOTIATION);
+
+ br_ssl_engine_set_versions(&cc->eng, BR_TLS12, BR_TLS12);
+ br_ssl_engine_set_suites(&cc->eng, suites, (sizeof suites) / (sizeof suites[0]));
+ br_ssl_client_set_default_rsapub(cc);
+ br_ssl_engine_set_default_rsavrfy(&cc->eng);
+
+ // install hashes
+ br_ssl_engine_set_hash(&cc->eng, br_sha256_ID, &br_sha256_vtable);
+ br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf);
+
+ // AES CTR/GCM small version, not contstant time (we don't really care here as there is no TPM anyways)
+ br_ssl_engine_set_gcm(&cc->eng, &br_sslrec_in_gcm_vtable, &br_sslrec_out_gcm_vtable);
+ br_ssl_engine_set_aes_ctr(&cc->eng, &br_aes_small_ctr_vtable);
+ br_ssl_engine_set_ghash(&cc->eng, &br_ghash_ctmul32);
+
+#ifdef USE_MQTT_TLS_FORCE_EC_CIPHER
+ // we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt unless forced
+ br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15);
+#endif
+ }
+}
+
+// Called by connect() to do the actual SSL setup and handshake.
+// Returns if the SSL handshake succeeded.
+bool WiFiClientSecure_light::_connectSSL(const char* hostName) {
+// #ifdef USE_MQTT_AWS_IOT
+// if ((!_chain_P) || (!_sk_ec_P)) {
+// setLastError(ERR_MISSING_EC_KEY);
+// return false;
+// }
+// #endif
+
+ // Validation context, either full CA validation or checking only fingerprints
+#ifdef USE_MQTT_TLS_CA_CERT
+ br_x509_minimal_context *x509_minimal;
+#else
+ br_x509_pubkeyfingerprint_context *x509_insecure;
+#endif
+
+ LOG_HEAP_SIZE("_connectSSL.start");
+
+ do { // used to exit on Out of Memory error and keep all cleanup code at the same place
+ // ============================================================
+ // allocate Thunk stack, move to alternate stack and initialize
+ stack_thunk_light_add_ref();
+ LOG_HEAP_SIZE("Thunk allocated");
+ DEBUG_BSSL("_connectSSL: start connection\n");
+ _freeSSL();
+ clearLastError();
+ if (!stack_thunk_light_get_stack_bot()) break;
+
+ _ctx_present = true;
+ _eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr
+
+ br_ssl_client_base_init(_sc.get());
+
+ // ============================================================
+ // Allocatte and initialize Decoder Context
+ LOG_HEAP_SIZE("_connectSSL before DecoderContext allocation");
+ // Only failure possible in the installation is OOM
+ #ifdef USE_MQTT_TLS_CA_CERT
+ x509_minimal = (br_x509_minimal_context*) malloc(sizeof(br_x509_minimal_context));
+ if (!x509_minimal) break;
+ br_x509_minimal_init(x509_minimal, &br_sha256_vtable, _ta_P, 1);
+ br_x509_minimal_set_rsa(x509_minimal, br_ssl_engine_get_rsavrfy(_eng));
+ br_x509_minimal_set_hash(x509_minimal, br_sha256_ID, &br_sha256_vtable);
+ br_ssl_engine_set_x509(_eng, &x509_minimal->vtable);
+
+ #else
+ x509_insecure = (br_x509_pubkeyfingerprint_context*) malloc(sizeof(br_x509_pubkeyfingerprint_context));
+ //x509_insecure = std::unique_ptr(new br_x509_pubkeyfingerprint_context);
+ if (!x509_insecure) break;
+ br_x509_pubkeyfingerprint_init(x509_insecure, _fingerprint1, _fingerprint2, _recv_fingerprint, _fingerprint_any);
+ br_ssl_engine_set_x509(_eng, &x509_insecure->vtable);
+ #endif
+ LOG_HEAP_SIZE("_connectSSL after DecoderContext allocation");
+
+ // ============================================================
+ // Set send/receive buffers
+ br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size);
+
+ // ============================================================
+ // allocate Private key if needed, only if USE_MQTT_AWS_IOT
+ LOG_HEAP_SIZE("_connectSSL before PrivKey allocation");
+ #ifdef USE_MQTT_AWS_IOT
+ // ============================================================
+ // Set the EC Private Key, only USE_MQTT_AWS_IOT
+ // limited to P256 curve
+ br_ssl_client_set_single_ec(_sc.get(), _chain_P, 1,
+ _sk_ec_P, _allowed_usages,
+ _cert_issuer_key_type, &br_ec_p256_m15, br_ecdsa_sign_asn1_get_default());
+ #endif // USE_MQTT_AWS_IOT
+
+ // ============================================================
+ // Start TLS connection, ALL
+ if (!br_ssl_client_reset(_sc.get(), hostName, 0)) break;
+
+ auto ret = _wait_for_handshake();
+ #ifdef DEBUG_ESP_SSL
+ if (!ret) {
+ DEBUG_BSSL("Couldn't connect. Error = %d\n", getLastError());
+ } else {
+ DEBUG_BSSL("Connected! MFLNStatus = %d\n", getMFLNStatus());
+ }
+ #endif
+ LOG_HEAP_SIZE("_connectSSL.end");
+ _max_thunkstack_use = stack_thunk_light_get_max_usage();
+ stack_thunk_light_del_ref();
+ //stack_thunk_light_repaint();
+ LOG_HEAP_SIZE("_connectSSL.end, freeing StackThunk");
+
+ #ifdef USE_MQTT_TLS_CA_CERT
+ free(x509_minimal);
+ #else
+ free(x509_insecure);
+ #endif
+ LOG_HEAP_SIZE("_connectSSL after release of Priv Key");
+ return ret;
+ } while (0);
+
+ // ============================================================
+ // if we arrived here, this means we had an OOM error, cleaning up
+ setLastError(ERR_OOM);
+ DEBUG_BSSL("_connectSSL: Out of memory\n");
+ stack_thunk_light_del_ref();
+#ifdef USE_MQTT_TLS_CA_CERT
+ free(x509_minimal);
+#else
+ free(x509_insecure);
+#endif
+ LOG_HEAP_SIZE("_connectSSL clean_on_error");
+ return false;
+}
+
+};
+
+#include "t_bearssl_tasmota_config.h"
+
+#endif // USE_TLS
diff --git a/tasmota/WiFiClientSecureLightBearSSL.h b/tasmota/WiFiClientSecureLightBearSSL.h
old mode 100644
new mode 100755
index 5dd0df35e..e5908275e
--- a/tasmota/WiFiClientSecureLightBearSSL.h
+++ b/tasmota/WiFiClientSecureLightBearSSL.h
@@ -1,221 +1,221 @@
-/*
- WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries
- - Mostly compatible with Arduino WiFi shield library and standard
- WiFiClient/ServerSecure (except for certificate handling).
-
- Copyright (c) 2018 Earle F. Philhower, III
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-*/
-
-#include
-
-#ifndef wificlientlightbearssl_h
-#define wificlientlightbearssl_h
-#if defined(USE_MQTT_TLS) || defined (USE_SENDMAIL)
-#include
-#include "WiFiClient.h"
-#include
-
-namespace BearSSL {
-
-class WiFiClientSecure_light : public WiFiClient {
- public:
- WiFiClientSecure_light(int recv, int xmit);
- ~WiFiClientSecure_light() override;
-
- void allocateBuffers(void);
-
- int connect(IPAddress ip, uint16_t port) override;
- int connect(const char* name, uint16_t port) override;
-
- uint8_t connected() override;
- size_t write(const uint8_t *buf, size_t size) override;
- size_t write_P(PGM_P buf, size_t size) override;
- size_t write(const char *buf) {
- return write((const uint8_t*)buf, strlen(buf));
- }
- size_t write_P(const char *buf) {
- return write_P((PGM_P)buf, strlen_P(buf));
- }
- size_t write(Stream& stream); // Note this is not virtual
- int read(uint8_t *buf, size_t size) override;
- int available() override;
- int read() override;
- int peek() override;
- size_t peekBytes(uint8_t *buffer, size_t length) override;
- bool flush(unsigned int maxWaitMs);
- bool stop(unsigned int maxWaitMs);
- void flush() override { (void)flush(0); }
- void stop() override { (void)stop(0); }
-
- // Only check SHA1 fingerprint of public key
- void setPubKeyFingerprint(const uint8_t *f1, const uint8_t *f2,
- bool f_any = false) {
- _fingerprint1 = f1;
- _fingerprint2 = f2;
- _fingerprint_any = f_any;
- }
- const uint8_t * getRecvPubKeyFingerprint(void) {
- return _recv_fingerprint;
- }
-
- void setClientECCert(const br_x509_certificate *cert, const br_ec_private_key *sk,
- unsigned allowed_usages, unsigned cert_issuer_key_type);
-
- void setTrustAnchor(const br_x509_trust_anchor *ta);
-
- // Sets the requested buffer size for transmit and receive
- void setBufferSizes(int recv, int xmit);
-
- // Returns whether MFLN negotiation for the above buffer sizes succeeded (after connection)
- int getMFLNStatus() {
- return connected() && br_ssl_engine_get_mfln_negotiated(_eng);
- }
-
- int32_t getLastError(void) {
- if (_last_error) {
- return _last_error;
- } else {
- return br_ssl_engine_last_error(_eng);
- }
- }
- inline void setLastError(int32_t err) {
- _last_error = err;
- }
- inline void clearLastError(void) {
- _last_error = 0;
- }
- inline size_t getMaxThunkStackUse(void) {
- return _max_thunkstack_use;
- }
-
- private:
- void _clear();
- bool _ctx_present;
- std::shared_ptr _sc;
- inline bool ctx_present() {
- return _ctx_present;
- }
- br_ssl_engine_context *_eng; // &_sc->eng, to allow for client or server contexts
- std::shared_ptr _iobuf_in;
- std::shared_ptr _iobuf_out;
- time_t _now;
- int _iobuf_in_size;
- int _iobuf_out_size;
- bool _handshake_done;
- uint64_t _last_error;
-
- bool _fingerprint_any; // accept all fingerprints
- const uint8_t *_fingerprint1; // fingerprint1 to be checked against
- const uint8_t *_fingerprint2; // fingerprint2 to be checked against
- uint8_t _recv_fingerprint[20]; // fingerprint received
-
- unsigned char *_recvapp_buf;
- size_t _recvapp_len;
-
- bool _clientConnected(); // Is the underlying socket alive?
- bool _connectSSL(const char *hostName); // Do initial SSL handshake
- void _freeSSL();
- int _run_until(unsigned target, bool blocking = true);
- size_t _write(const uint8_t *buf, size_t size, bool pmem);
- bool _wait_for_handshake(); // Sets and return the _handshake_done after connecting
-
- // Optional client certificate
- const br_x509_certificate *_chain_P; // PROGMEM certificate
- const br_ec_private_key *_sk_ec_P; // PROGMEM private key
- const br_x509_trust_anchor *_ta_P; // PROGMEM server CA
- unsigned _allowed_usages;
- unsigned _cert_issuer_key_type;
-
- // record the maximum use of ThunkStack for monitoring
- size_t _max_thunkstack_use;
-
-};
-
-#define ERR_OOM -1000
-#define ERR_CANT_RESOLVE_IP -1001
-#define ERR_TCP_CONNECT -1002
-#define ERR_MISSING_EC_KEY -1003
-#define ERR_MISSING_CA -1004
-
-// For reference, BearSSL error codes:
-// #define BR_ERR_OK 0
-// #define BR_ERR_BAD_PARAM 1
-// #define BR_ERR_BAD_STATE 2
-// #define BR_ERR_UNSUPPORTED_VERSION 3
-// #define BR_ERR_BAD_VERSION 4
-// #define BR_ERR_BAD_LENGTH 5
-// #define BR_ERR_TOO_LARGE 6
-// #define BR_ERR_BAD_MAC 7
-// #define BR_ERR_NO_RANDOM 8
-// #define BR_ERR_UNKNOWN_TYPE 9
-// #define BR_ERR_UNEXPECTED 10
-// #define BR_ERR_BAD_CCS 12
-// #define BR_ERR_BAD_ALERT 13
-// #define BR_ERR_BAD_HANDSHAKE 14
-// #define BR_ERR_OVERSIZED_ID 15
-// #define BR_ERR_BAD_CIPHER_SUITE 16
-// #define BR_ERR_BAD_COMPRESSION 17
-// #define BR_ERR_BAD_FRAGLEN 18
-// #define BR_ERR_BAD_SECRENEG 19
-// #define BR_ERR_EXTRA_EXTENSION 20
-// #define BR_ERR_BAD_SNI 21
-// #define BR_ERR_BAD_HELLO_DONE 22
-// #define BR_ERR_LIMIT_EXCEEDED 23
-// #define BR_ERR_BAD_FINISHED 24
-// #define BR_ERR_RESUME_MISMATCH 25
-// #define BR_ERR_INVALID_ALGORITHM 26
-// #define BR_ERR_BAD_SIGNATURE 27
-// #define BR_ERR_WRONG_KEY_USAGE 28
-// #define BR_ERR_NO_CLIENT_AUTH 29
-// #define BR_ERR_IO 31
-// #define BR_ERR_RECV_FATAL_ALERT 256
-// #define BR_ERR_SEND_FATAL_ALERT 512
-// #define BR_ERR_X509_OK 32
-// #define BR_ERR_X509_INVALID_VALUE 33
-// #define BR_ERR_X509_TRUNCATED 34
-// #define BR_ERR_X509_EMPTY_CHAIN 35
-// #define BR_ERR_X509_INNER_TRUNC 36
-// #define BR_ERR_X509_BAD_TAG_CLASS 37
-// #define BR_ERR_X509_BAD_TAG_VALUE 38
-// #define BR_ERR_X509_INDEFINITE_LENGTH 39
-// #define BR_ERR_X509_EXTRA_ELEMENT 40
-// #define BR_ERR_X509_UNEXPECTED 41
-// #define BR_ERR_X509_NOT_CONSTRUCTED 42
-// #define BR_ERR_X509_NOT_PRIMITIVE 43
-// #define BR_ERR_X509_PARTIAL_BYTE 44
-// #define BR_ERR_X509_BAD_BOOLEAN 45
-// #define BR_ERR_X509_OVERFLOW 46
-// #define BR_ERR_X509_BAD_DN 47
-// #define BR_ERR_X509_BAD_TIME 48
-// #define BR_ERR_X509_UNSUPPORTED 49
-// #define BR_ERR_X509_LIMIT_EXCEEDED 50
-// #define BR_ERR_X509_WRONG_KEY_TYPE 51
-// #define BR_ERR_X509_BAD_SIGNATURE 52
-// #define BR_ERR_X509_TIME_UNKNOWN 53
-// #define BR_ERR_X509_EXPIRED 54
-// #define BR_ERR_X509_DN_MISMATCH 55
-// #define BR_ERR_X509_BAD_SERVER_NAME 56
-// #define BR_ERR_X509_CRITICAL_EXTENSION 57
-// #define BR_ERR_X509_NOT_CA 58
-// #define BR_ERR_X509_FORBIDDEN_KEY_USAGE 59
-// #define BR_ERR_X509_WEAK_PUBLIC_KEY 60
-// #define BR_ERR_X509_NOT_TRUSTED 62
-
-};
-
-#endif // USE_MQTT_TLS
-#endif // wificlientlightbearssl_h
+/*
+ WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries
+ - Mostly compatible with Arduino WiFi shield library and standard
+ WiFiClient/ServerSecure (except for certificate handling).
+
+ Copyright (c) 2018 Earle F. Philhower, III
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include
+
+#ifndef wificlientlightbearssl_h
+#define wificlientlightbearssl_h
+#ifdef USE_TLS
+#include
+#include "WiFiClient.h"
+#include
+
+namespace BearSSL {
+
+class WiFiClientSecure_light : public WiFiClient {
+ public:
+ WiFiClientSecure_light(int recv, int xmit);
+ ~WiFiClientSecure_light() override;
+
+ void allocateBuffers(void);
+
+ int connect(IPAddress ip, uint16_t port) override;
+ int connect(const char* name, uint16_t port) override;
+
+ uint8_t connected() override;
+ size_t write(const uint8_t *buf, size_t size) override;
+ size_t write_P(PGM_P buf, size_t size) override;
+ size_t write(const char *buf) {
+ return write((const uint8_t*)buf, strlen(buf));
+ }
+ size_t write_P(const char *buf) {
+ return write_P((PGM_P)buf, strlen_P(buf));
+ }
+ size_t write(Stream& stream); // Note this is not virtual
+ int read(uint8_t *buf, size_t size) override;
+ int available() override;
+ int read() override;
+ int peek() override;
+ size_t peekBytes(uint8_t *buffer, size_t length) override;
+ bool flush(unsigned int maxWaitMs);
+ bool stop(unsigned int maxWaitMs);
+ void flush() override { (void)flush(0); }
+ void stop() override { (void)stop(0); }
+
+ // Only check SHA1 fingerprint of public key
+ void setPubKeyFingerprint(const uint8_t *f1, const uint8_t *f2,
+ bool f_any = false) {
+ _fingerprint1 = f1;
+ _fingerprint2 = f2;
+ _fingerprint_any = f_any;
+ }
+ const uint8_t * getRecvPubKeyFingerprint(void) {
+ return _recv_fingerprint;
+ }
+
+ void setClientECCert(const br_x509_certificate *cert, const br_ec_private_key *sk,
+ unsigned allowed_usages, unsigned cert_issuer_key_type);
+
+ void setTrustAnchor(const br_x509_trust_anchor *ta);
+
+ // Sets the requested buffer size for transmit and receive
+ void setBufferSizes(int recv, int xmit);
+
+ // Returns whether MFLN negotiation for the above buffer sizes succeeded (after connection)
+ int getMFLNStatus() {
+ return connected() && br_ssl_engine_get_mfln_negotiated(_eng);
+ }
+
+ int32_t getLastError(void) {
+ if (_last_error) {
+ return _last_error;
+ } else {
+ return br_ssl_engine_last_error(_eng);
+ }
+ }
+ inline void setLastError(int32_t err) {
+ _last_error = err;
+ }
+ inline void clearLastError(void) {
+ _last_error = 0;
+ }
+ inline size_t getMaxThunkStackUse(void) {
+ return _max_thunkstack_use;
+ }
+
+ private:
+ void _clear();
+ bool _ctx_present;
+ std::shared_ptr _sc;
+ inline bool ctx_present() {
+ return _ctx_present;
+ }
+ br_ssl_engine_context *_eng; // &_sc->eng, to allow for client or server contexts
+ std::shared_ptr _iobuf_in;
+ std::shared_ptr _iobuf_out;
+ time_t _now;
+ int _iobuf_in_size;
+ int _iobuf_out_size;
+ bool _handshake_done;
+ uint64_t _last_error;
+
+ bool _fingerprint_any; // accept all fingerprints
+ const uint8_t *_fingerprint1; // fingerprint1 to be checked against
+ const uint8_t *_fingerprint2; // fingerprint2 to be checked against
+ uint8_t _recv_fingerprint[20]; // fingerprint received
+
+ unsigned char *_recvapp_buf;
+ size_t _recvapp_len;
+
+ bool _clientConnected(); // Is the underlying socket alive?
+ bool _connectSSL(const char *hostName); // Do initial SSL handshake
+ void _freeSSL();
+ int _run_until(unsigned target, bool blocking = true);
+ size_t _write(const uint8_t *buf, size_t size, bool pmem);
+ bool _wait_for_handshake(); // Sets and return the _handshake_done after connecting
+
+ // Optional client certificate
+ const br_x509_certificate *_chain_P; // PROGMEM certificate
+ const br_ec_private_key *_sk_ec_P; // PROGMEM private key
+ const br_x509_trust_anchor *_ta_P; // PROGMEM server CA
+ unsigned _allowed_usages;
+ unsigned _cert_issuer_key_type;
+
+ // record the maximum use of ThunkStack for monitoring
+ size_t _max_thunkstack_use;
+
+};
+
+#define ERR_OOM -1000
+#define ERR_CANT_RESOLVE_IP -1001
+#define ERR_TCP_CONNECT -1002
+// #define ERR_MISSING_EC_KEY -1003 // deprecated, AWS IoT is not called if the private key is not present
+#define ERR_MISSING_CA -1004
+
+// For reference, BearSSL error codes:
+// #define BR_ERR_OK 0
+// #define BR_ERR_BAD_PARAM 1
+// #define BR_ERR_BAD_STATE 2
+// #define BR_ERR_UNSUPPORTED_VERSION 3
+// #define BR_ERR_BAD_VERSION 4
+// #define BR_ERR_BAD_LENGTH 5
+// #define BR_ERR_TOO_LARGE 6
+// #define BR_ERR_BAD_MAC 7
+// #define BR_ERR_NO_RANDOM 8
+// #define BR_ERR_UNKNOWN_TYPE 9
+// #define BR_ERR_UNEXPECTED 10
+// #define BR_ERR_BAD_CCS 12
+// #define BR_ERR_BAD_ALERT 13
+// #define BR_ERR_BAD_HANDSHAKE 14
+// #define BR_ERR_OVERSIZED_ID 15
+// #define BR_ERR_BAD_CIPHER_SUITE 16
+// #define BR_ERR_BAD_COMPRESSION 17
+// #define BR_ERR_BAD_FRAGLEN 18
+// #define BR_ERR_BAD_SECRENEG 19
+// #define BR_ERR_EXTRA_EXTENSION 20
+// #define BR_ERR_BAD_SNI 21
+// #define BR_ERR_BAD_HELLO_DONE 22
+// #define BR_ERR_LIMIT_EXCEEDED 23
+// #define BR_ERR_BAD_FINISHED 24
+// #define BR_ERR_RESUME_MISMATCH 25
+// #define BR_ERR_INVALID_ALGORITHM 26
+// #define BR_ERR_BAD_SIGNATURE 27
+// #define BR_ERR_WRONG_KEY_USAGE 28
+// #define BR_ERR_NO_CLIENT_AUTH 29
+// #define BR_ERR_IO 31
+// #define BR_ERR_RECV_FATAL_ALERT 256
+// #define BR_ERR_SEND_FATAL_ALERT 512
+// #define BR_ERR_X509_OK 32
+// #define BR_ERR_X509_INVALID_VALUE 33
+// #define BR_ERR_X509_TRUNCATED 34
+// #define BR_ERR_X509_EMPTY_CHAIN 35
+// #define BR_ERR_X509_INNER_TRUNC 36
+// #define BR_ERR_X509_BAD_TAG_CLASS 37
+// #define BR_ERR_X509_BAD_TAG_VALUE 38
+// #define BR_ERR_X509_INDEFINITE_LENGTH 39
+// #define BR_ERR_X509_EXTRA_ELEMENT 40
+// #define BR_ERR_X509_UNEXPECTED 41
+// #define BR_ERR_X509_NOT_CONSTRUCTED 42
+// #define BR_ERR_X509_NOT_PRIMITIVE 43
+// #define BR_ERR_X509_PARTIAL_BYTE 44
+// #define BR_ERR_X509_BAD_BOOLEAN 45
+// #define BR_ERR_X509_OVERFLOW 46
+// #define BR_ERR_X509_BAD_DN 47
+// #define BR_ERR_X509_BAD_TIME 48
+// #define BR_ERR_X509_UNSUPPORTED 49
+// #define BR_ERR_X509_LIMIT_EXCEEDED 50
+// #define BR_ERR_X509_WRONG_KEY_TYPE 51
+// #define BR_ERR_X509_BAD_SIGNATURE 52
+// #define BR_ERR_X509_TIME_UNKNOWN 53
+// #define BR_ERR_X509_EXPIRED 54
+// #define BR_ERR_X509_DN_MISMATCH 55
+// #define BR_ERR_X509_BAD_SERVER_NAME 56
+// #define BR_ERR_X509_CRITICAL_EXTENSION 57
+// #define BR_ERR_X509_NOT_CA 58
+// #define BR_ERR_X509_FORBIDDEN_KEY_USAGE 59
+// #define BR_ERR_X509_WEAK_PUBLIC_KEY 60
+// #define BR_ERR_X509_NOT_TRUSTED 62
+
+};
+
+#endif // USE_TLS
+#endif // wificlientlightbearssl_h
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 6e20a598e..4a75b4b98 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -753,4 +753,16 @@
#error "Select either USE_RULES or USE_SCRIPT. They can't both be used at the same time"
#endif
+/*********************************************************************************************\
+ * Post-process compile options for TLS
+\*********************************************************************************************/
+
+#if defined(USE_MQTT_TLS) || defined(USE_SENDMAIL) || defined(USE_TELEGRAM)
+ #define USE_TLS // flag indicates we need to include TLS code
+
+ #if defined(USE_MQTT_AWS_IOT) || defined(USE_TELEGRAM)
+ #define USE_MQTT_TLS_FORCE_EC_CIPHER // AWS IoT and TELEGRAM require EC Cipher
+ #endif
+#endif
+
#endif // _MY_USER_CONFIG_H_
diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino
index ecf1127ec..78c50edc3 100644
--- a/tasmota/tasmota.ino
+++ b/tasmota/tasmota.ino
@@ -36,9 +36,9 @@
#include "tasmota_version.h" // Tasmota version information
#include "tasmota.h" // Enumeration used in my_user_config.h
#include "my_user_config.h" // Fixed user configurable options
-#ifdef USE_MQTT_TLS
+#ifdef USE_TLS
#include // We need to include before "tasmota_globals.h" to take precedence over the BearSSL version in Arduino
-#endif // USE_MQTT_TLS
+#endif // USE_TLS
#include "tasmota_globals.h" // Function prototypes and global configuration
#include "i18n.h" // Language support configured by my_user_config.h
#include "tasmota_template.h" // Hardware configuration
diff --git a/tasmota/tasmota_ca.ino b/tasmota/tasmota_ca.ino
index 8e75f891e..1db0a8c63 100644
--- a/tasmota/tasmota_ca.ino
+++ b/tasmota/tasmota_ca.ino
@@ -21,9 +21,8 @@
// Please use fingerprint validation instead
// However, the CA are available below for future use if it appears to be useful
-#ifdef USE_MQTT_TLS_CA_CERT
+#if defined(USE_TLS) && defined(USE_MQTT_TLS_CA_CERT)
-#ifndef USE_MQTT_AWS_IOT
/*********************************************************************************************\
* LetsEncrypt IdenTrust DST Root CA X3 certificate, RSA 2048 bits SHA 256, valid until 20210417
*
@@ -35,7 +34,7 @@
* remove "static" and add "PROGMEM"
\*********************************************************************************************/
-static const unsigned char PROGMEM TA0_DN[] = {
+static const unsigned char PROGMEM LetsEncrypt_DN[] = {
0x30, 0x4A, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A,
0x13, 0x0D, 0x4C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6E, 0x63, 0x72,
@@ -45,7 +44,7 @@ static const unsigned char PROGMEM TA0_DN[] = {
0x79, 0x20, 0x58, 0x33
};
-static const unsigned char PROGMEM TA0_RSA_N[] = {
+static const unsigned char PROGMEM LetsEncrypt_RSA_N[] = {
0x9C, 0xD3, 0x0C, 0xF0, 0x5A, 0xE5, 0x2E, 0x47, 0xB7, 0x72, 0x5D, 0x37,
0x83, 0xB3, 0x68, 0x63, 0x30, 0xEA, 0xD7, 0x35, 0x26, 0x19, 0x25, 0xE1,
0xBD, 0xBE, 0x35, 0xF1, 0x70, 0x92, 0x2F, 0xB7, 0xB8, 0x4B, 0x41, 0x05,
@@ -70,27 +69,22 @@ static const unsigned char PROGMEM TA0_RSA_N[] = {
0xD8, 0x7D, 0xC3, 0x93
};
-static const unsigned char TA0_RSA_E[] = {
+static const unsigned char LetsEncrypt_RSA_E[] = {
0x01, 0x00, 0x01
};
static const br_x509_trust_anchor PROGMEM LetsEncryptX3CrossSigned_TA = {
- { (unsigned char *)TA0_DN, sizeof TA0_DN },
+ { (unsigned char *)LetsEncrypt_DN, sizeof LetsEncrypt_DN },
BR_X509_TA_CA,
{
BR_KEYTYPE_RSA,
{ .rsa = {
- (unsigned char *)TA0_RSA_N, sizeof TA0_RSA_N,
- (unsigned char *)TA0_RSA_E, sizeof TA0_RSA_E,
+ (unsigned char *)LetsEncrypt_RSA_N, sizeof LetsEncrypt_RSA_N,
+ (unsigned char *)LetsEncrypt_RSA_E, sizeof LetsEncrypt_RSA_E,
} }
}
};
-#define TAs_NUM 1
-
-#endif // not USE_MQTT_AWS_IOT
-
-#ifdef USE_MQTT_AWS_IOT
/*********************************************************************************************\
* Amazon Root CA, RSA 2048 bits SHA 256, valid until 20380117
*
@@ -103,7 +97,7 @@ static const br_x509_trust_anchor PROGMEM LetsEncryptX3CrossSigned_TA = {
\*********************************************************************************************/
-const unsigned char PROGMEM TA0_DN[] = {
+const unsigned char PROGMEM AmazonRootCA1_DN[] = {
0x30, 0x39, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x55, 0x53, 0x31, 0x0F, 0x30, 0x0D, 0x06, 0x03, 0x55, 0x04, 0x0A,
0x13, 0x06, 0x41, 0x6D, 0x61, 0x7A, 0x6F, 0x6E, 0x31, 0x19, 0x30, 0x17,
@@ -111,7 +105,7 @@ const unsigned char PROGMEM TA0_DN[] = {
0x6E, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x41, 0x20, 0x31
};
-const unsigned char PROGMEM TA0_RSA_N[] = {
+const unsigned char PROGMEM AmazonRootCA1_RSA_N[] = {
0xB2, 0x78, 0x80, 0x71, 0xCA, 0x78, 0xD5, 0xE3, 0x71, 0xAF, 0x47, 0x80,
0x50, 0x74, 0x7D, 0x6E, 0xD8, 0xD7, 0x88, 0x76, 0xF4, 0x99, 0x68, 0xF7,
0x58, 0x21, 0x60, 0xF9, 0x74, 0x84, 0x01, 0x2F, 0xAC, 0x02, 0x2D, 0x86,
@@ -136,24 +130,79 @@ const unsigned char PROGMEM TA0_RSA_N[] = {
0x9A, 0xC8, 0xAA, 0x0D
};
-static const unsigned char PROGMEM TA0_RSA_E[] = {
+static const unsigned char PROGMEM AmazonRootCA1_RSA_E[] = {
0x01, 0x00, 0x01
};
const br_x509_trust_anchor PROGMEM AmazonRootCA1_TA = {
- { (unsigned char *)TA0_DN, sizeof TA0_DN },
+ { (unsigned char *)AmazonRootCA1_DN, sizeof AmazonRootCA1_DN },
BR_X509_TA_CA,
{
BR_KEYTYPE_RSA,
{ .rsa = {
- (unsigned char *)TA0_RSA_N, sizeof TA0_RSA_N,
- (unsigned char *)TA0_RSA_E, sizeof TA0_RSA_E,
+ (unsigned char *)AmazonRootCA1_RSA_N, sizeof AmazonRootCA1_RSA_N,
+ (unsigned char *)AmazonRootCA1_RSA_E, sizeof AmazonRootCA1_RSA_E,
} }
}
};
-#define TAs_NUM 1
+// we add a separate CA for telegram
+/*********************************************************************************************\
+ * GoDaddy Daddy Secure Certificate Authority - G2, RSA 2048 bits SHA 256, valid until 20220523
+ *
+ * to convert do: "brssl ta GoDaddyCA.pem"
+ * then copy and paste below, chain the generic names to the same as below
+ * remove "static" and add "PROGMEM"
+\*********************************************************************************************/
-#endif // USE_MQTT_AWS_IOT
+const unsigned char GoDaddyCAG2_DN[] PROGMEM = {
+ 0x30, 0x3E, 0x31, 0x21, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13,
+ 0x18, 0x44, 0x6F, 0x6D, 0x61, 0x69, 0x6E, 0x20, 0x43, 0x6F, 0x6E, 0x74,
+ 0x72, 0x6F, 0x6C, 0x20, 0x56, 0x61, 0x6C, 0x69, 0x64, 0x61, 0x74, 0x65,
+ 0x64, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10,
+ 0x61, 0x70, 0x69, 0x2E, 0x74, 0x65, 0x6C, 0x65, 0x67, 0x72, 0x61, 0x6D,
+ 0x2E, 0x6F, 0x72, 0x67
+};
-#endif // USE_MQTT_TLS_CA_CERT
+const unsigned char GoDaddyCAG2_RSA_N[] PROGMEM = {
+ 0xB4, 0xA3, 0x16, 0x9E, 0x5C, 0x57, 0xC9, 0x89, 0x65, 0xED, 0xEA, 0x78,
+ 0x0B, 0xAE, 0x8A, 0x58, 0x2F, 0xAE, 0x5A, 0xC8, 0x6E, 0x49, 0x8D, 0xFC,
+ 0x57, 0xA5, 0x98, 0x88, 0x78, 0x2E, 0x0B, 0x3C, 0x40, 0x3C, 0x21, 0x2E,
+ 0x9A, 0x94, 0x98, 0x33, 0xA7, 0xE3, 0x42, 0xA7, 0x85, 0xFA, 0xD0, 0x73,
+ 0x84, 0x01, 0x1C, 0x72, 0x39, 0x37, 0x23, 0xB5, 0x56, 0x1D, 0x43, 0xA5,
+ 0x71, 0x14, 0x08, 0x24, 0xA5, 0x39, 0xCC, 0xDE, 0x58, 0x53, 0x94, 0x8E,
+ 0x2A, 0x42, 0xA7, 0x4E, 0x2D, 0x07, 0x32, 0x9E, 0xBA, 0x8B, 0xD3, 0x2A,
+ 0xA9, 0x9E, 0xC0, 0xE3, 0xCE, 0x9A, 0x10, 0x96, 0x45, 0x58, 0x7A, 0xC7,
+ 0x1E, 0x45, 0x14, 0x23, 0x92, 0xBB, 0x54, 0x82, 0x88, 0x94, 0x49, 0xB6,
+ 0xBE, 0x81, 0x21, 0x00, 0x29, 0x6D, 0xC9, 0xCE, 0x8B, 0x39, 0x3A, 0xDC,
+ 0x35, 0x15, 0xD9, 0xEB, 0x47, 0x9C, 0xEF, 0xBA, 0x09, 0x0E, 0x16, 0xE4,
+ 0xD9, 0xEB, 0x72, 0x30, 0xFA, 0x49, 0xAB, 0x98, 0x31, 0x7C, 0xB3, 0xAC,
+ 0x2B, 0x29, 0x91, 0x87, 0x08, 0x41, 0x72, 0x5E, 0x35, 0xC7, 0x87, 0x04,
+ 0x22, 0xF5, 0x48, 0x76, 0x30, 0x6D, 0x88, 0xDF, 0xF2, 0xA5, 0x29, 0x13,
+ 0x70, 0xB3, 0x87, 0x02, 0xD5, 0x6B, 0x58, 0xB1, 0xE8, 0x73, 0xC7, 0xE4,
+ 0xEF, 0x79, 0x86, 0xA4, 0x07, 0x5F, 0x67, 0xB4, 0x79, 0x8D, 0xA4, 0x25,
+ 0x01, 0x82, 0x8C, 0xE0, 0x30, 0x17, 0xCB, 0x4B, 0x5C, 0xFB, 0xEB, 0x4C,
+ 0x12, 0x51, 0xB9, 0xC9, 0x04, 0x1F, 0x7E, 0xD2, 0xF8, 0xBA, 0xF5, 0x35,
+ 0x8D, 0x8A, 0x1C, 0x37, 0x82, 0xF0, 0x15, 0x73, 0x00, 0x6E, 0x3D, 0x1C,
+ 0x76, 0x8B, 0x01, 0x74, 0x81, 0x3D, 0xE4, 0x2C, 0xA7, 0xCC, 0x2F, 0x66,
+ 0xDC, 0x44, 0xA8, 0x27, 0x3F, 0xEA, 0xD0, 0xA7, 0xA8, 0xF1, 0xCB, 0xEA,
+ 0xDA, 0x07, 0x38, 0xBD
+};
+
+const unsigned char GoDaddyCAG2_RSA_E[] PROGMEM = {
+ 0x01, 0x00, 0x01
+};
+
+const br_x509_trust_anchor GoDaddyCAG2_TA PROGMEM = {
+ { (unsigned char *)GoDaddyCAG2_DN, sizeof GoDaddyCAG2_DN },
+ 0,
+ {
+ BR_KEYTYPE_RSA,
+ { .rsa = {
+ (unsigned char *)GoDaddyCAG2_RSA_N, sizeof GoDaddyCAG2_RSA_N,
+ (unsigned char *)GoDaddyCAG2_RSA_E, sizeof GoDaddyCAG2_RSA_E,
+ } }
+ }
+};
+
+#endif // defined(USE_TLS) && defined(USE_MQTT_TLS_CA_CERT)
diff --git a/tasmota/tasmota_globals.h b/tasmota/tasmota_globals.h
index 44826fa64..f33024c45 100644
--- a/tasmota/tasmota_globals.h
+++ b/tasmota/tasmota_globals.h
@@ -88,7 +88,7 @@ extern "C" void resetPins();
const uint16_t WEB_LOG_SIZE = 4000; // Max number of characters in weblog
#endif
-#if defined(USE_MQTT_TLS) && defined(ARDUINO_ESP8266_RELEASE_2_3_0)
+#if defined(USE_TLS) && defined(ARDUINO_ESP8266_RELEASE_2_3_0)
#error "TLS is no more supported on Core 2.3.0, use 2.4.2 or higher."
#endif
From dc8d354f161c74fefbcbf4a22b78364d4b25a720 Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Wed, 10 Jun 2020 21:14:18 +0200
Subject: [PATCH 11/35] Fix asm for gcc17
---
tasmota/StackThunk_light.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/StackThunk_light.h b/tasmota/StackThunk_light.h
index 13a0cb7d3..164417000 100644
--- a/tasmota/StackThunk_light.h
+++ b/tasmota/StackThunk_light.h
@@ -52,7 +52,7 @@ extern uint32_t stack_thunk_light_refcnt;
// Thunking macro
#define make_stack_thunk_light(fcnToThunk) \
-__asm("\n\
+__asm__("\n\
.text\n\
.literal_position\n\
.literal .LC_STACK_VALUE"#fcnToThunk", 0xdeadbeef\n\
From 8cbb35ec4fbb00e29e754c0602c75af1a2c70fce Mon Sep 17 00:00:00 2001
From: Robert Jaakke
Date: Wed, 10 Jun 2020 21:54:32 +0200
Subject: [PATCH 12/35] Removed m_initFail check because we moved it to begin()
---
lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp | 21 ---------------------
1 file changed, 21 deletions(-)
diff --git a/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp b/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp
index b73e12b83..c6f8aeb1d 100644
--- a/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp
+++ b/lib/LOLIN_HP303B/src/LOLIN_HP303B.cpp
@@ -147,11 +147,6 @@ uint8_t LOLIN_HP303B::getRevisionId(void)
*/
int16_t LOLIN_HP303B::standby(void)
{
- //abort if initialization failed
- if(m_initFail)
- {
- return HP303B__FAIL_INIT_FAILED;
- }
//set device to idling mode
int16_t ret = setOpMode(IDLE);
if(ret != HP303B__SUCCEEDED)
@@ -270,11 +265,6 @@ int16_t LOLIN_HP303B::startMeasureTempOnce(void)
*/
int16_t LOLIN_HP303B::startMeasureTempOnce(uint8_t oversamplingRate)
{
- //abort if initialization failed
- if(m_initFail)
- {
- return HP303B__FAIL_INIT_FAILED;
- }
//abort if device is not in idling mode
if(m_opMode!=IDLE)
{
@@ -394,11 +384,6 @@ int16_t LOLIN_HP303B::startMeasurePressureOnce(void)
*/
int16_t LOLIN_HP303B::startMeasurePressureOnce(uint8_t oversamplingRate)
{
- //abort if initialization failed
- if(m_initFail)
- {
- return HP303B__FAIL_INIT_FAILED;
- }
//abort if device is not in idling mode
if(m_opMode != IDLE)
{
@@ -428,12 +413,6 @@ int16_t LOLIN_HP303B::startMeasurePressureOnce(uint8_t oversamplingRate)
*/
int16_t LOLIN_HP303B::getSingleResult(float &result)
{
- //abort if initialization failed
- if(m_initFail)
- {
- return HP303B__FAIL_INIT_FAILED;
- }
-
//read finished bit for current opMode
int16_t rdy;
switch(m_opMode)
From 440219fd91afef1b763a02efc80cadd9663d369d Mon Sep 17 00:00:00 2001
From: Phil Dubach
Date: Wed, 10 Jun 2020 19:55:49 -0700
Subject: [PATCH 13/35] Fix thermostat when using local sensor
Macros are not expanded in string constants, so the thermostat driver
never managed to obtain the current temperature from the local sensor
(SensorInputSet 1).
---
tasmota/xdrv_39_thermostat.ino | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/xdrv_39_thermostat.ino b/tasmota/xdrv_39_thermostat.ino
index 79e842dc5..768088117 100644
--- a/tasmota/xdrv_39_thermostat.ino
+++ b/tasmota/xdrv_39_thermostat.ino
@@ -1331,7 +1331,7 @@ void ThermostatGetLocalSensor(uint8_t ctr_output) {
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.parseObject((const char*)mqtt_data);
if (root.success()) {
- const char* value_c = root["THERMOSTAT_SENSOR_NAME"]["Temperature"];
+ const char* value_c = root[THERMOSTAT_SENSOR_NAME]["Temperature"];
if (value_c != NULL && strlen(value_c) > 0 && (isdigit(value_c[0]) || (value_c[0] == '-' && isdigit(value_c[1])) ) ) {
int16_t value = (int16_t)(CharToFloat(value_c) * 10);
if ( (value >= -1000)
From 2453beb30f7e6b1792b2ac073deb84fd7b16b9af Mon Sep 17 00:00:00 2001
From: gemu2015
Date: Thu, 11 Jun 2020 06:43:24 +0200
Subject: [PATCH 14/35] scripter fix sdcard regression
---
tasmota/xdrv_10_scripter.ino | 29 +++++++++++++----------------
1 file changed, 13 insertions(+), 16 deletions(-)
diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino
index 5a79d50b5..4d0bc1588 100755
--- a/tasmota/xdrv_10_scripter.ino
+++ b/tasmota/xdrv_10_scripter.ino
@@ -3979,8 +3979,7 @@ void ListDir(char *path, uint8_t depth) {
char path[48];
-void Script_FileUploadConfiguration(void)
-{
+void Script_FileUploadConfiguration(void) {
uint8_t depth=0;
strcpy(path,"/");
@@ -3995,17 +3994,6 @@ void Script_FileUploadConfiguration(void)
}
}
- void ScriptFileUploadSuccess(void) {
- WSContentStart_P(S_INFORMATION);
- WSContentSendStyle();
- WSContentSend_P(PSTR("" D_UPLOAD " " D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS));
- WSContentSend_P(PSTR("
"));
- WSContentSend_P(PSTR("
"),"/upl",D_UPL_DONE);
- //WSContentSpaceButton(BUTTON_MAIN);
- WSContentStop();
- }
-
WSContentStart_P(S_SCRIPT_FILE_UPLOAD);
WSContentSendStyle();
WSContentSend_P(HTTP_FORM_FILE_UPLOAD,D_SDCARD_DIR);
@@ -4023,13 +4011,22 @@ void Script_FileUploadConfiguration(void)
Web.upload_error = 0;
}
+void ScriptFileUploadSuccess(void) {
+ WSContentStart_P(S_INFORMATION);
+ WSContentSendStyle();
+ WSContentSend_P(PSTR("" D_UPLOAD " " D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS));
+ WSContentSend_P(PSTR("
"));
+ WSContentSend_P(PSTR(""),"/upl",D_UPL_DONE);
+ //WSContentSpaceButton(BUTTON_MAIN);
+ WSContentStop();
+}
+
+
File upload_file;
-
void script_upload(void) {
-
//AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: file upload"));
-
HTTPUpload& upload = Webserver->upload();
if (upload.status == UPLOAD_FILE_START) {
char npath[48];
From ce4f987367461dd1cdc16627bbd039050b6f0ad4 Mon Sep 17 00:00:00 2001
From: Staars
Date: Thu, 11 Jun 2020 09:55:07 +0200
Subject: [PATCH 15/35] bugfix
---
tasmota/xsns_48_chirp.ino | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/tasmota/xsns_48_chirp.ino b/tasmota/xsns_48_chirp.ino
index 9606b419c..934dff205 100644
--- a/tasmota/xsns_48_chirp.ino
+++ b/tasmota/xsns_48_chirp.ino
@@ -20,6 +20,8 @@
Version Date Action Description
--------------------------------------------------------------------------------------------
+ 1.0.0.2 20200611 changed - bugfix: decouple restart of the work loop from FUNC_JSON_APPEND callback
+ ---
1.0.0.1 20190917 changed - rework of the inner loop to enable delays in the middle of I2C-reads
changed - double send address change only for fw>0x25
changed - use DEBUG_SENSOR_LOG, change ILLUMINANCE to DARKNESS
@@ -300,7 +302,7 @@ void ChirpServiceAllSensors(uint8_t job){
void ChirpEvery100MSecond(void)
{
- // DEBUG_SENSOR_LOG(PSTR("CHIRP: every second"));
+ // DEBUG_SENSOR_LOG(PSTR("CHIRP: every 100 mseconds, counter: %u, next job: %u"),chirp_timeout_count,chirp_next_job);
if(chirp_timeout_count == 0) { //countdown complete, now do something
switch(chirp_next_job) {
case 0: //this should only be called after driver initialization
@@ -377,10 +379,11 @@ void ChirpEvery100MSecond(void)
break;
case 13:
DEBUG_SENSOR_LOG(PSTR("CHIRP: paused, waiting for TELE"));
+ chirp_next_job++;
break;
case 14:
if (Settings.tele_period > 16){
- chirp_timeout_count = (Settings.tele_period - 17) * 10; // sync it with the TELEPERIOD, we need about up to 17 seconds to measure
+ chirp_timeout_count = (Settings.tele_period - 16) * 10; // sync it with the TELEPERIOD, we need about up to 16 seconds to measure
DEBUG_SENSOR_LOG(PSTR("CHIRP: timeout 1/10 sec: %u, tele: %u"), chirp_timeout_count, Settings.tele_period);
}
else{
@@ -533,7 +536,6 @@ bool Xsns48(uint8_t function)
break;
case FUNC_JSON_APPEND:
ChirpShow(1);
- chirp_next_job = 14; // TELE done, now compute time for next measure cycle
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR:
From 4c5b2f37fdbcb742083165e25d92f5cb4c69a051 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Thu, 11 Jun 2020 17:30:33 +0200
Subject: [PATCH 16/35] Add initial support for Telegram
Add initial support for Telegram bot (#8619)
---
RELEASENOTES.md | 3 +-
tasmota/CHANGELOG.md | 4 +
tasmota/my_user_config.h | 5 +
tasmota/support_features.ino | 4 +-
tasmota/tasmota.h | 13 +-
tasmota/tasmota_version.h | 2 +-
tasmota/xdrv_40_telegram.ino | 479 +++++++++++++++++++++++++++++++++++
tools/decode-status.py | 4 +-
8 files changed, 503 insertions(+), 11 deletions(-)
create mode 100644 tasmota/xdrv_40_telegram.ino
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index fdac7edde..ab01ddaa4 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -52,7 +52,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
## Changelog
-### Version 8.3.1.2
+### Version 8.3.1.3
- Change IRremoteESP8266 library updated to v2.7.7
- Change Adafruit_SGP30 library from v1.0.3 to v1.2.0 (#8519)
@@ -76,3 +76,4 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
- Add support for up to two BH1750 sensors controlled by commands ``BH1750Resolution`` and ``BH1750MTime`` (#8139)
- Add support for up to eight MCP9808 temperature sensors by device111 (#8594)
- Add support for BL0940 energy monitor as used in Blitzwolf BW-SHP10 (#8175)
+- Add initial support for Telegram bot (#8619)
diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md
index 4f7d3132b..9033a2bba 100644
--- a/tasmota/CHANGELOG.md
+++ b/tasmota/CHANGELOG.md
@@ -1,5 +1,9 @@
## Unreleased (development)
+### 8.3.1.3 20200611
+
+- Add initial support for Telegram bot (#8619)
+
### 8.3.1.2 20200522
- Change Energy JSON Total field from ``"Total":[33.736,11.717,16.978]`` to ``"Total":33.736,"TotalTariff":[11.717,16.978]``
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 4a75b4b98..ea2e6eefe 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -367,6 +367,11 @@
// Full documentation here: https://github.com/arendst/Tasmota/wiki/AWS-IoT
// #define USE_4K_RSA // Support 4096 bits certificates, instead of 2048
+// -- Telegram Protocol ---------------------------
+//#define USE_TELEGRAM // Support for Telegram protocol (+49k code, +7.0k mem and +4.8k additional during connection handshake)
+ #define USE_TELEGRAM_FINGERPRINT "\xB2\x72\x47\xA6\x69\x8C\x3C\x69\xF9\x58\x6C\xF3\x60\x02\xFB\x83\xFA\x8B\x1F\x23" // Telegram api.telegram.org TLS public key fingerpring
+// #define USE_MQTT_TLS_CA_CERT // Use certificate instead of fingerprint
+
// -- KNX IP Protocol -----------------------------
//#define USE_KNX // Enable KNX IP Protocol Support (+9.4k code, +3k7 mem)
#define USE_KNX_WEB_MENU // Enable KNX WEB MENU (+8.3k code, +144 mem)
diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino
index 20bb9acf3..04de532d6 100644
--- a/tasmota/support_features.ino
+++ b/tasmota/support_features.ino
@@ -575,7 +575,9 @@ void GetFeatures(void)
#ifdef USE_BL0940
feature6 |= 0x00004000; // xnrg_14_bl0940.ino
#endif
-// feature6 |= 0x00008000;
+#ifdef USE_TELEGRAM
+ feature6 |= 0x00008000; // xdrv_40_telegram.ino
+#endif
// feature6 |= 0x00010000;
// feature6 |= 0x00020000;
diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h
index c3913e8a1..49347f1f8 100644
--- a/tasmota/tasmota.h
+++ b/tasmota/tasmota.h
@@ -293,7 +293,7 @@ enum SettingsTextIndex { SET_OTAURL,
SET_TEMPLATE_NAME,
SET_DEV_GROUP_NAME1, SET_DEV_GROUP_NAME2, SET_DEV_GROUP_NAME3, SET_DEV_GROUP_NAME4,
SET_DEVICENAME,
- SET_TELEGRAMTOKEN,
+ SET_TELEGRAM_TOKEN, SET_TELEGRAM_CHATID,
SET_MAX };
enum DevGroupMessageType { DGR_MSGTYP_FULL_STATUS, DGR_MSGTYP_PARTIAL_UPDATE, DGR_MSGTYP_UPDATE, DGR_MSGTYP_UPDATE_MORE_TO_COME, DGR_MSGTYP_UPDATE_DIRECT, DGR_MSGTYPE_UPDATE_COMMAND };
@@ -321,9 +321,10 @@ enum DevGroupShareItem { DGR_SHARE_POWER = 1, DGR_SHARE_LIGHT_BRI = 2, DGR_SHARE
enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER,
SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_REMOTE, SRC_SHUTTER,
- SRC_THERMOSTAT, SRC_MAX };
+ SRC_THERMOSTAT, SRC_CHAT, SRC_MAX };
const char kCommandSource[] PROGMEM = "I|MQTT|Restart|Button|Switch|Backlog|Serial|WebGui|WebCommand|WebConsole|PulseTimer|"
- "Timer|Rule|MaxPower|MaxEnergy|Overtemp|Light|Knx|Display|Wemo|Hue|Retry|Remote|Shutter|Thermostat";
+ "Timer|Rule|MaxPower|MaxEnergy|Overtemp|Light|Knx|Display|Wemo|Hue|Retry|Remote|Shutter|"
+ "Thermostat|Chat";
const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 };
@@ -344,10 +345,10 @@ const SerConfu8 kTasmotaSerialConfig[] PROGMEM = {
SERIAL_5O2, SERIAL_6O2, SERIAL_7O2, SERIAL_8O2
};
-enum TuyaSupportedFunctions { TUYA_MCU_FUNC_NONE, TUYA_MCU_FUNC_SWT1 = 1, TUYA_MCU_FUNC_SWT2, TUYA_MCU_FUNC_SWT3, TUYA_MCU_FUNC_SWT4,
- TUYA_MCU_FUNC_REL1 = 11, TUYA_MCU_FUNC_REL2, TUYA_MCU_FUNC_REL3, TUYA_MCU_FUNC_REL4, TUYA_MCU_FUNC_REL5,
+enum TuyaSupportedFunctions { TUYA_MCU_FUNC_NONE, TUYA_MCU_FUNC_SWT1 = 1, TUYA_MCU_FUNC_SWT2, TUYA_MCU_FUNC_SWT3, TUYA_MCU_FUNC_SWT4,
+ TUYA_MCU_FUNC_REL1 = 11, TUYA_MCU_FUNC_REL2, TUYA_MCU_FUNC_REL3, TUYA_MCU_FUNC_REL4, TUYA_MCU_FUNC_REL5,
TUYA_MCU_FUNC_REL6, TUYA_MCU_FUNC_REL7, TUYA_MCU_FUNC_REL8, TUYA_MCU_FUNC_DIMMER = 21, TUYA_MCU_FUNC_POWER = 31,
- TUYA_MCU_FUNC_CURRENT, TUYA_MCU_FUNC_VOLTAGE, TUYA_MCU_FUNC_BATTERY_STATE, TUYA_MCU_FUNC_BATTERY_PERCENTAGE,
+ TUYA_MCU_FUNC_CURRENT, TUYA_MCU_FUNC_VOLTAGE, TUYA_MCU_FUNC_BATTERY_STATE, TUYA_MCU_FUNC_BATTERY_PERCENTAGE,
TUYA_MCU_FUNC_REL1_INV = 41, TUYA_MCU_FUNC_REL2_INV, TUYA_MCU_FUNC_REL3_INV, TUYA_MCU_FUNC_REL4_INV, TUYA_MCU_FUNC_REL5_INV,
TUYA_MCU_FUNC_REL6_INV, TUYA_MCU_FUNC_REL7_INV, TUYA_MCU_FUNC_REL8_INV, TUYA_MCU_FUNC_LOWPOWER_MODE = 51, TUYA_MCU_FUNC_LAST = 255
};
diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h
index 1b21293b2..f22a0288b 100644
--- a/tasmota/tasmota_version.h
+++ b/tasmota/tasmota_version.h
@@ -20,7 +20,7 @@
#ifndef _TASMOTA_VERSION_H_
#define _TASMOTA_VERSION_H_
-const uint32_t VERSION = 0x08030102;
+const uint32_t VERSION = 0x08030103;
// Lowest compatible version
const uint32_t VERSION_COMPATIBLE = 0x07010006;
diff --git a/tasmota/xdrv_40_telegram.ino b/tasmota/xdrv_40_telegram.ino
new file mode 100644
index 000000000..a2ec2e49f
--- /dev/null
+++ b/tasmota/xdrv_40_telegram.ino
@@ -0,0 +1,479 @@
+/*
+ xdrv_40_telegram.ino - telegram for Tasmota
+
+ Copyright (C) 2020 Theo Arends
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifdef USE_TELEGRAM
+/*********************************************************************************************\
+ * Telegram bot
+ *
+ * Supported commands:
+ * TGToken - Add your BotFather created bot token (default none)
+ * TGChatId - Add your BotFather created bot chat id (default none)
+ * TGPoll - Telegram receive poll time (default 10 seconds)
+ * TGState 0 - Disable telegram sending (default)
+ * TGState 1 - Enable telegram sending
+ * TGState 2 - Disable telegram listener (default)
+ * TGState 3 - Enable telegram listener
+ * TGState 4 - Disable telegram response echo (default)
+ * TGState 5 - Enable telegram response echo
+ * TGSend - If telegram sending is enabled AND a chat id is present then send data
+ *
+ * Tested with defines
+ * #define USE_TELEGRAM // Support for Telegram protocol
+ * #define USE_TELEGRAM_FINGERPRINT "\xB2\x72\x47\xA6\x69\x8C\x3C\x69\xF9\x58\x6C\xF3\x60\x02\xFB\x83\xFA\x8B\x1F\x23" // Telegram api.telegram.org TLS public key fingerpring
+\*********************************************************************************************/
+
+#define XDRV_40 40
+
+#define TELEGRAM_SEND_RETRY 4 // Retries
+#define TELEGRAM_LOOP_WAIT 10 // Seconds
+
+#ifdef USE_MQTT_TLS_CA_CERT
+ static const uint32_t tls_rx_size = 2048; // since Telegram CA is bigger than 1024 bytes, we need to increase rx buffer
+ static const uint32_t tls_tx_size = 1024;
+#else
+ static const uint32_t tls_rx_size = 1024;
+ static const uint32_t tls_tx_size = 1024;
+#endif
+
+#include "WiFiClientSecureLightBearSSL.h"
+BearSSL::WiFiClientSecure_light *telegramClient = nullptr;
+
+static const uint8_t Telegram_Fingerprint[] PROGMEM = USE_TELEGRAM_FINGERPRINT;
+
+struct {
+ String message[3][6]; // amount of messages read per time (update_id, name_id, name, lastname, chat_id, text)
+ uint8_t state = 0;
+ uint8_t index = 0;
+ uint8_t retry = 0;
+ uint8_t poll = TELEGRAM_LOOP_WAIT;
+ uint8_t wait = 0;
+ bool send_enable = false;
+ bool recv_enable = false;
+ bool echo_enable = false;
+ bool recv_busy = false;
+} Telegram;
+
+bool TelegramInit(void) {
+ bool init_done = false;
+ if (strlen(SettingsText(SET_TELEGRAM_TOKEN))) {
+ if (!telegramClient) {
+ telegramClient = new BearSSL::WiFiClientSecure_light(tls_rx_size, tls_tx_size);
+#ifdef USE_MQTT_TLS_CA_CERT
+ telegramClient->setTrustAnchor(&GoDaddyCAG2_TA);
+#else
+ telegramClient->setPubKeyFingerprint(Telegram_Fingerprint, Telegram_Fingerprint, false); // check server fingerprint
+#endif
+
+ Telegram.message[0][0]="0"; // Number of received messages
+ Telegram.message[1][0]="";
+ Telegram.message[0][1]="0"; // Code of last read Message
+
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: Started"));
+ }
+
+ init_done = true;
+ }
+ return init_done;
+}
+
+/**************************************************************************************************
+ * function to achieve connection to api.telegram.org and send command to telegram *
+ * (Argument to pass: URL to address to Telegram) *
+ **************************************************************************************************/
+String TelegramConnectToTelegram(String command) {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: Cmnd %s"), command.c_str());
+
+ if (!TelegramInit()) { return ""; }
+
+ String response = "";
+ uint32_t tls_connect_time = millis();
+
+ if (telegramClient->connect("api.telegram.org", 443)) {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: Connected in %d ms, max ThunkStack used %d"),
+ millis() - tls_connect_time, telegramClient->getMaxThunkStackUse());
+
+ telegramClient->println("GET /"+command);
+
+ String a = "";
+ char c;
+ int ch_count=0;
+ uint32_t now = millis();
+ bool avail = false;
+ while (millis() -now < 1500) {
+ while (telegramClient->available()) {
+ char c = telegramClient->read();
+ if (ch_count < 700) {
+ response = response + c;
+ ch_count++;
+ }
+ avail = true;
+ }
+ if (avail) {
+ break;
+ }
+ }
+
+ telegramClient->stop();
+ }
+
+ return response;
+}
+
+/***************************************************************
+ * GetUpdates - function to receive all messages from telegram *
+ * (Argument to pass: the last+1 message to read) *
+ ***************************************************************/
+void TelegramGetUpdates(String offset) {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: getUpdates"));
+
+ if (!TelegramInit()) { return; }
+
+ String _token = SettingsText(SET_TELEGRAM_TOKEN);
+ String command = "bot" + _token + "/getUpdates?offset=" + offset;
+ String response = TelegramConnectToTelegram(command); //recieve reply from telegram.org
+
+ // {"ok":true,"result":[]}
+ // or
+ // {"ok":true,"result":[
+ // {"update_id":973125394,
+ // "message":{"message_id":25,
+ // "from":{"id":139920293,"is_bot":false,"first_name":"Theo","last_name":"Arends","username":"tjatja","language_code":"nl"},
+ // "chat":{"id":139920293,"first_name":"Theo","last_name":"Arends","username":"tjatja","type":"private"},
+ // "date":1591877503,
+ // "text":"M1"
+ // }
+ // },
+ // {"update_id":973125395,
+ // "message":{"message_id":26,
+ // "from":{"id":139920293,"is_bot":false,"first_name":"Theo","last_name":"Arends","username":"tjatja","language_code":"nl"},
+ // "chat":{"id":139920293,"first_name":"Theo","last_name":"Arends","username":"tjatja","type":"private"},
+ // "date":1591877508,
+ // "text":"M2"
+ // }
+ // }
+ // ]}
+ // or
+ // {"ok":true,"result":[
+ // {"update_id":973125396,
+ // "message":{"message_id":29,
+ // "from":{"id":139920293,"is_bot":false,"first_name":"Theo","last_name":"Arends","username":"tjatja","language_code":"nl"},
+ // "chat":{"id":139920293,"first_name":"Theo","last_name":"Arends","username":"tjatja","type":"private"},
+ // "date":1591879753,
+ // "text":"/power toggle",
+ // "entities":[{"offset":0,"length":6,"type":"bot_command"}]
+ // }
+ // }
+ // ]}
+
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TLG: Response %s"), response.c_str());
+
+ // parsing of reply from Telegram into separate received messages
+ int i = 0; //messages received counter
+ if (response != "") {
+
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: Sent Update request messages up to %s"), offset.c_str());
+
+ String a = "";
+ int ch_count = 0;
+ String c;
+ for (uint32_t n = 1; n < response.length() +1; n++) { //Search for each message start
+ ch_count++;
+ c = response.substring(n -1, n);
+ a = a + c;
+ if (ch_count > 8) {
+ if (a.substring(ch_count -9) == "update_id") {
+ if (i > 1) { break; }
+ Telegram.message[i][0] = a.substring(0, ch_count -11);
+ a = a.substring(ch_count-11);
+ i++;
+ ch_count = 11;
+ }
+ }
+ }
+ if (1 == i) {
+ Telegram.message[i][0] = a.substring(0, ch_count); //Assign of parsed message into message matrix if only 1 message)
+ }
+ if (i > 1) { i = i -1; }
+ }
+ //check result of parsing process
+ if (response == "") {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: Failed to update"));
+ return;
+ }
+ if (0 == i) {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: No new messages"));
+ Telegram.message[0][0] = "0";
+ } else {
+ Telegram.message[0][0] = String(i); //returns how many messages are in the array
+ for (int b = 1; b < i+1; b++) {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: Msg %d %s"), b, Telegram.message[b][0].c_str());
+ }
+
+ TelegramAnalizeMessage();
+ }
+}
+
+void TelegramAnalizeMessage(void) {
+ for (uint32_t i = 1; i < Telegram.message[0][0].toInt() +1; i++) {
+ Telegram.message[i][5] = "";
+
+ DynamicJsonBuffer jsonBuffer;
+ JsonObject &root = jsonBuffer.parseObject(Telegram.message[i][0]);
+ if (root.success()) {
+ Telegram.message[i][0] = root["update_id"].as();
+ Telegram.message[i][1] = root["message"]["from"]["id"].as();
+ Telegram.message[i][2] = root["message"]["from"]["first_name"].as();
+ Telegram.message[i][3] = root["message"]["from"]["last_name"].as();
+ Telegram.message[i][4] = root["message"]["chat"]["id"].as();
+ Telegram.message[i][5] = root["message"]["text"].as();
+ }
+
+ int id = Telegram.message[Telegram.message[0][0].toInt()][0].toInt() +1;
+ Telegram.message[0][1] = id; // Write id of last read message
+
+ for (int j = 0; j < 6; j++) {
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TLG: Parsed%d \"%s\""), j, Telegram.message[i][j].c_str());
+ }
+ }
+}
+
+bool TelegramSendMessage(String chat_id, String text) {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: sendMessage"));
+
+ if (!TelegramInit()) { return false; }
+
+ bool sent = false;
+ if (text != "") {
+ String _token = SettingsText(SET_TELEGRAM_TOKEN);
+ String command = "bot" + _token + "/sendMessage?chat_id=" + chat_id + "&text=" + text;
+ String response = TelegramConnectToTelegram(command);
+
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TLG: Response %s"), response.c_str());
+
+ if (response.startsWith("{\"ok\":true")) {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: Message sent"));
+ sent = true;
+ }
+
+ }
+
+ return sent;
+}
+
+/*
+void TelegramSendGetMe(void) {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: getMe"));
+
+ if (!TelegramInit()) { return; }
+
+ String _token = SettingsText(SET_TELEGRAM_TOKEN);
+ String command = "bot" + _token + "/getMe";
+ String response = TelegramConnectToTelegram(command);
+
+ // {"ok":true,"result":{"id":1179906608,"is_bot":true,"first_name":"Tasmota","username":"tasmota_bot","can_join_groups":true,"can_read_all_group_messages":false,"supports_inline_queries":false}}
+
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TLG: Response %s"), response.c_str());
+}
+*/
+
+String TelegramExecuteCommand(const char *svalue) {
+ String response = "";
+
+ uint32_t curridx = web_log_index;
+ ExecuteCommand(svalue, SRC_CHAT);
+ if (web_log_index != curridx) {
+ uint32_t counter = curridx;
+ response = F("{");
+ bool cflg = false;
+ do {
+ char* tmp;
+ size_t len;
+ GetLog(counter, &tmp, &len);
+ if (len) {
+ // [14:49:36 MQTT: stat/wemos5/RESULT = {"POWER":"OFF"}] > [{"POWER":"OFF"}]
+ char* JSON = (char*)memchr(tmp, '{', len);
+ if (JSON) { // Is it a JSON message (and not only [15:26:08 MQT: stat/wemos5/POWER = O])
+ size_t JSONlen = len - (JSON - tmp);
+ if (JSONlen > sizeof(mqtt_data)) { JSONlen = sizeof(mqtt_data); }
+ char stemp[JSONlen];
+ strlcpy(stemp, JSON +1, JSONlen -2);
+ if (cflg) { response += F(","); }
+ response += stemp;
+ cflg = true;
+ }
+ }
+ counter++;
+ counter &= 0xFF;
+ if (!counter) counter++; // Skip 0 as it is not allowed
+ } while (counter != web_log_index);
+ response += F("}");
+ } else {
+ response = F("{\"" D_RSLT_WARNING "\":\"" D_ENABLE_WEBLOG_FOR_RESPONSE "\"}");
+ }
+
+ return response;
+}
+
+void TelegramLoop(void) {
+ if (!global_state.wifi_down && (Telegram.recv_enable || Telegram.echo_enable)) {
+ switch (Telegram.state) {
+ case 0:
+ TelegramInit();
+ Telegram.state++;
+ break;
+ case 1:
+ TelegramGetUpdates(Telegram.message[0][1]); // launch API GetUpdates up to xxx message
+ Telegram.index = 1;
+ Telegram.retry = TELEGRAM_SEND_RETRY;
+ Telegram.state++;
+ break;
+ case 2:
+ if (Telegram.echo_enable) {
+ if (Telegram.retry && (Telegram.index < Telegram.message[0][0].toInt() + 1)) {
+ if (TelegramSendMessage(Telegram.message[Telegram.index][4], Telegram.message[Telegram.index][5])) {
+ Telegram.index++;
+ Telegram.retry = TELEGRAM_SEND_RETRY;
+ } else {
+ Telegram.retry--;
+ }
+ } else {
+ Telegram.message[0][0] = ""; // All messages have been replied - reset new messages
+ Telegram.wait = Telegram.poll;
+ Telegram.state++;
+ }
+ } else {
+ if (Telegram.message[0][0].toInt() && (Telegram.message[Telegram.index][5].length() > 0)) {
+ String logging = TelegramExecuteCommand(Telegram.message[Telegram.index][5].c_str());
+ if (logging.length() > 0) {
+ TelegramSendMessage(Telegram.message[Telegram.index][4], logging);
+ }
+ }
+ Telegram.message[0][0] = ""; // All messages have been replied - reset new messages
+ Telegram.wait = Telegram.poll;
+ Telegram.state++;
+ }
+ break;
+ case 3:
+ if (Telegram.wait) {
+ Telegram.wait--;
+ } else {
+ Telegram.state = 1;
+ }
+ }
+ }
+}
+
+/*********************************************************************************************\
+ * Commands
+\*********************************************************************************************/
+
+#define D_CMND_TGSTATE "State"
+#define D_CMND_TGPOLL "Poll"
+#define D_CMND_TGSEND "Send"
+#define D_CMND_TGTOKEN "Token"
+#define D_CMND_TGCHATID "ChatId"
+
+const char kTelegramCommands[] PROGMEM = "TG|" // Prefix
+ D_CMND_TGSTATE "|" D_CMND_TGPOLL "|" D_CMND_TGTOKEN "|" D_CMND_TGCHATID "|" D_CMND_TGSEND;
+
+void (* const TelegramCommand[])(void) PROGMEM = {
+ &CmndTgState, &CmndTgPoll, &CmndTgToken, &CmndTgChatId, &CmndTgSend };
+
+void CmndTgState(void) {
+ if (XdrvMailbox.data_len > 0) {
+ if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 6)) {
+ switch (XdrvMailbox.payload) {
+ case 0: // Off
+ case 1: // On
+ Telegram.send_enable = XdrvMailbox.payload &1;
+ break;
+ case 2: // Off
+ case 3: // On
+ Telegram.recv_enable = XdrvMailbox.payload &1;
+ break;
+ case 4: // Off
+ case 5: // On
+ Telegram.echo_enable = XdrvMailbox.payload &1;
+ break;
+ }
+ }
+ }
+ snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":{\"Send\":\"%s\",\"Receive\":\"%s\",\"Echo\":\"%s\"}}"),
+ XdrvMailbox.command, GetStateText(Telegram.send_enable), GetStateText(Telegram.recv_enable), GetStateText(Telegram.echo_enable));
+}
+
+void CmndTgPoll(void) {
+ if ((XdrvMailbox.payload >= 4) && (XdrvMailbox.payload <= 300)) {
+ Telegram.poll = XdrvMailbox.payload;
+ if (Telegram.poll < Telegram.wait) {
+ Telegram.wait = Telegram.poll;
+ }
+ }
+ ResponseCmndNumber(Telegram.poll);
+}
+
+void CmndTgToken(void) {
+ if (XdrvMailbox.data_len > 0) {
+ SettingsUpdateText(SET_TELEGRAM_TOKEN, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data);
+ }
+ ResponseCmndChar(SettingsText(SET_TELEGRAM_TOKEN));
+}
+
+void CmndTgChatId(void) {
+ if (XdrvMailbox.data_len > 0) {
+ SettingsUpdateText(SET_TELEGRAM_CHATID, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data);
+ }
+ ResponseCmndChar(SettingsText(SET_TELEGRAM_CHATID));
+}
+
+void CmndTgSend(void) {
+ if (!Telegram.send_enable || !strlen(SettingsText(SET_TELEGRAM_CHATID))) {
+ ResponseCmndChar(D_JSON_FAILED);
+ return;
+ }
+ if (XdrvMailbox.data_len > 0) {
+ String message = XdrvMailbox.data;
+ String chat_id = SettingsText(SET_TELEGRAM_CHATID);
+ if (!TelegramSendMessage(chat_id, message)) {
+ ResponseCmndChar(D_JSON_FAILED);
+ return;
+ }
+ }
+ ResponseCmndDone();
+}
+
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+
+bool Xdrv40(uint8_t function)
+{
+ bool result = false;
+
+ switch (function) {
+ case FUNC_EVERY_SECOND:
+ TelegramLoop();
+ break;
+ case FUNC_COMMAND:
+ result = DecodeCommand(kTelegramCommands, TelegramCommand);
+ break;
+ }
+ return result;
+}
+#endif // USE_TELEGRAM
diff --git a/tools/decode-status.py b/tools/decode-status.py
index 1ce04951e..4228b69df 100755
--- a/tools/decode-status.py
+++ b/tools/decode-status.py
@@ -205,7 +205,7 @@ a_features = [[
"USE_KEELOQ","USE_HRXL","USE_SONOFF_D1","USE_HDC1080",
"USE_IAQ","USE_DISPLAY_SEVENSEG","USE_AS3935","USE_PING",
"USE_WINDMETER","USE_OPENTHERM","USE_THERMOSTAT","USE_VEML6075",
- "USE_VEML7700","USE_MCP9808","USE_BL0940","",
+ "USE_VEML7700","USE_MCP9808","USE_BL0940","USE_TELEGRAM",
"","","","",
"","","","",
"","","","",
@@ -243,7 +243,7 @@ else:
obj = json.load(fp)
def StartDecode():
- print ("\n*** decode-status.py v20200607 by Theo Arends and Jacek Ziolkowski ***")
+ print ("\n*** decode-status.py v20200611 by Theo Arends and Jacek Ziolkowski ***")
# print("Decoding\n{}".format(obj))
From 18b00f9cbeca16572cf79f6506cfc57114f8fceb Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Thu, 11 Jun 2020 17:52:10 +0200
Subject: [PATCH 17/35] Change telegram command prefix to Tm
---
tasmota/xdrv_40_telegram.ino | 76 ++++++++++++++++++------------------
1 file changed, 38 insertions(+), 38 deletions(-)
diff --git a/tasmota/xdrv_40_telegram.ino b/tasmota/xdrv_40_telegram.ino
index a2ec2e49f..ce0d83f10 100644
--- a/tasmota/xdrv_40_telegram.ino
+++ b/tasmota/xdrv_40_telegram.ino
@@ -22,16 +22,16 @@
* Telegram bot
*
* Supported commands:
- * TGToken - Add your BotFather created bot token (default none)
- * TGChatId - Add your BotFather created bot chat id (default none)
- * TGPoll - Telegram receive poll time (default 10 seconds)
- * TGState 0 - Disable telegram sending (default)
- * TGState 1 - Enable telegram sending
- * TGState 2 - Disable telegram listener (default)
- * TGState 3 - Enable telegram listener
- * TGState 4 - Disable telegram response echo (default)
- * TGState 5 - Enable telegram response echo
- * TGSend - If telegram sending is enabled AND a chat id is present then send data
+ * TmToken - Add your BotFather created bot token (default none)
+ * TmChatId - Add your BotFather created bot chat id (default none)
+ * TmPoll - Telegram receive poll time (default 10 seconds)
+ * TmState 0 - Disable telegram sending (default)
+ * TmState 1 - Enable telegram sending
+ * TmState 2 - Disable telegram listener (default)
+ * TmState 3 - Enable telegram listener
+ * TmState 4 - Disable telegram response echo (default)
+ * TmState 5 - Enable telegram response echo
+ * TmSend - If telegram sending is enabled AND a chat id is present then send data
*
* Tested with defines
* #define USE_TELEGRAM // Support for Telegram protocol
@@ -84,7 +84,7 @@ bool TelegramInit(void) {
Telegram.message[1][0]="";
Telegram.message[0][1]="0"; // Code of last read Message
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: Started"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Started"));
}
init_done = true;
@@ -97,7 +97,7 @@ bool TelegramInit(void) {
* (Argument to pass: URL to address to Telegram) *
**************************************************************************************************/
String TelegramConnectToTelegram(String command) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: Cmnd %s"), command.c_str());
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Cmnd %s"), command.c_str());
if (!TelegramInit()) { return ""; }
@@ -105,7 +105,7 @@ String TelegramConnectToTelegram(String command) {
uint32_t tls_connect_time = millis();
if (telegramClient->connect("api.telegram.org", 443)) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: Connected in %d ms, max ThunkStack used %d"),
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Connected in %d ms, max ThunkStack used %d"),
millis() - tls_connect_time, telegramClient->getMaxThunkStackUse());
telegramClient->println("GET /"+command);
@@ -140,7 +140,7 @@ String TelegramConnectToTelegram(String command) {
* (Argument to pass: the last+1 message to read) *
***************************************************************/
void TelegramGetUpdates(String offset) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: getUpdates"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: getUpdates"));
if (!TelegramInit()) { return; }
@@ -181,13 +181,13 @@ void TelegramGetUpdates(String offset) {
// }
// ]}
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TLG: Response %s"), response.c_str());
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: Response %s"), response.c_str());
// parsing of reply from Telegram into separate received messages
int i = 0; //messages received counter
if (response != "") {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: Sent Update request messages up to %s"), offset.c_str());
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Sent Update request messages up to %s"), offset.c_str());
String a = "";
int ch_count = 0;
@@ -213,16 +213,16 @@ void TelegramGetUpdates(String offset) {
}
//check result of parsing process
if (response == "") {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: Failed to update"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Failed to update"));
return;
}
if (0 == i) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: No new messages"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: No new messages"));
Telegram.message[0][0] = "0";
} else {
Telegram.message[0][0] = String(i); //returns how many messages are in the array
for (int b = 1; b < i+1; b++) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: Msg %d %s"), b, Telegram.message[b][0].c_str());
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Msg %d %s"), b, Telegram.message[b][0].c_str());
}
TelegramAnalizeMessage();
@@ -248,13 +248,13 @@ void TelegramAnalizeMessage(void) {
Telegram.message[0][1] = id; // Write id of last read message
for (int j = 0; j < 6; j++) {
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TLG: Parsed%d \"%s\""), j, Telegram.message[i][j].c_str());
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: Parsed%d \"%s\""), j, Telegram.message[i][j].c_str());
}
}
}
bool TelegramSendMessage(String chat_id, String text) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: sendMessage"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: sendMessage"));
if (!TelegramInit()) { return false; }
@@ -264,10 +264,10 @@ bool TelegramSendMessage(String chat_id, String text) {
String command = "bot" + _token + "/sendMessage?chat_id=" + chat_id + "&text=" + text;
String response = TelegramConnectToTelegram(command);
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TLG: Response %s"), response.c_str());
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: Response %s"), response.c_str());
if (response.startsWith("{\"ok\":true")) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: Message sent"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Message sent"));
sent = true;
}
@@ -278,7 +278,7 @@ bool TelegramSendMessage(String chat_id, String text) {
/*
void TelegramSendGetMe(void) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TLG: getMe"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: getMe"));
if (!TelegramInit()) { return; }
@@ -288,7 +288,7 @@ void TelegramSendGetMe(void) {
// {"ok":true,"result":{"id":1179906608,"is_bot":true,"first_name":"Tasmota","username":"tasmota_bot","can_join_groups":true,"can_read_all_group_messages":false,"supports_inline_queries":false}}
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TLG: Response %s"), response.c_str());
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: Response %s"), response.c_str());
}
*/
@@ -383,19 +383,19 @@ void TelegramLoop(void) {
* Commands
\*********************************************************************************************/
-#define D_CMND_TGSTATE "State"
-#define D_CMND_TGPOLL "Poll"
-#define D_CMND_TGSEND "Send"
-#define D_CMND_TGTOKEN "Token"
-#define D_CMND_TGCHATID "ChatId"
+#define D_CMND_TMSTATE "State"
+#define D_CMND_TMPOLL "Poll"
+#define D_CMND_TMSEND "Send"
+#define D_CMND_TMTOKEN "Token"
+#define D_CMND_TMCHATID "ChatId"
-const char kTelegramCommands[] PROGMEM = "TG|" // Prefix
- D_CMND_TGSTATE "|" D_CMND_TGPOLL "|" D_CMND_TGTOKEN "|" D_CMND_TGCHATID "|" D_CMND_TGSEND;
+const char kTelegramCommands[] PROGMEM = "Tm|" // Prefix
+ D_CMND_TMSTATE "|" D_CMND_TMPOLL "|" D_CMND_TMTOKEN "|" D_CMND_TMCHATID "|" D_CMND_TMSEND;
void (* const TelegramCommand[])(void) PROGMEM = {
- &CmndTgState, &CmndTgPoll, &CmndTgToken, &CmndTgChatId, &CmndTgSend };
+ &CmndTmState, &CmndTmPoll, &CmndTmToken, &CmndTmChatId, &CmndTmSend };
-void CmndTgState(void) {
+void CmndTmState(void) {
if (XdrvMailbox.data_len > 0) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 6)) {
switch (XdrvMailbox.payload) {
@@ -418,7 +418,7 @@ void CmndTgState(void) {
XdrvMailbox.command, GetStateText(Telegram.send_enable), GetStateText(Telegram.recv_enable), GetStateText(Telegram.echo_enable));
}
-void CmndTgPoll(void) {
+void CmndTmPoll(void) {
if ((XdrvMailbox.payload >= 4) && (XdrvMailbox.payload <= 300)) {
Telegram.poll = XdrvMailbox.payload;
if (Telegram.poll < Telegram.wait) {
@@ -428,21 +428,21 @@ void CmndTgPoll(void) {
ResponseCmndNumber(Telegram.poll);
}
-void CmndTgToken(void) {
+void CmndTmToken(void) {
if (XdrvMailbox.data_len > 0) {
SettingsUpdateText(SET_TELEGRAM_TOKEN, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data);
}
ResponseCmndChar(SettingsText(SET_TELEGRAM_TOKEN));
}
-void CmndTgChatId(void) {
+void CmndTmChatId(void) {
if (XdrvMailbox.data_len > 0) {
SettingsUpdateText(SET_TELEGRAM_CHATID, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data);
}
ResponseCmndChar(SettingsText(SET_TELEGRAM_CHATID));
}
-void CmndTgSend(void) {
+void CmndTmSend(void) {
if (!Telegram.send_enable || !strlen(SettingsText(SET_TELEGRAM_CHATID))) {
ResponseCmndChar(D_JSON_FAILED);
return;
From 24cd4276256aeaefe9fd67ca34978bcf81ac2f73 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Thu, 11 Jun 2020 18:18:03 +0200
Subject: [PATCH 18/35] Remove debugging info from telegram
---
tasmota/xdrv_40_telegram.ino | 39 ++++++++++++++----------------------
1 file changed, 15 insertions(+), 24 deletions(-)
diff --git a/tasmota/xdrv_40_telegram.ino b/tasmota/xdrv_40_telegram.ino
index ce0d83f10..97c5aa453 100644
--- a/tasmota/xdrv_40_telegram.ino
+++ b/tasmota/xdrv_40_telegram.ino
@@ -84,7 +84,7 @@ bool TelegramInit(void) {
Telegram.message[1][0]="";
Telegram.message[0][1]="0"; // Code of last read Message
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Started"));
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("TGM: Started"));
}
init_done = true;
@@ -92,12 +92,8 @@ bool TelegramInit(void) {
return init_done;
}
-/**************************************************************************************************
- * function to achieve connection to api.telegram.org and send command to telegram *
- * (Argument to pass: URL to address to Telegram) *
- **************************************************************************************************/
String TelegramConnectToTelegram(String command) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Cmnd %s"), command.c_str());
+// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Cmnd %s"), command.c_str());
if (!TelegramInit()) { return ""; }
@@ -105,8 +101,7 @@ String TelegramConnectToTelegram(String command) {
uint32_t tls_connect_time = millis();
if (telegramClient->connect("api.telegram.org", 443)) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Connected in %d ms, max ThunkStack used %d"),
- millis() - tls_connect_time, telegramClient->getMaxThunkStackUse());
+// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Connected in %d ms, max ThunkStack used %d"), millis() - tls_connect_time, telegramClient->getMaxThunkStackUse());
telegramClient->println("GET /"+command);
@@ -135,12 +130,8 @@ String TelegramConnectToTelegram(String command) {
return response;
}
-/***************************************************************
- * GetUpdates - function to receive all messages from telegram *
- * (Argument to pass: the last+1 message to read) *
- ***************************************************************/
void TelegramGetUpdates(String offset) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: getUpdates"));
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: getUpdates"));
if (!TelegramInit()) { return; }
@@ -181,13 +172,13 @@ void TelegramGetUpdates(String offset) {
// }
// ]}
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: Response %s"), response.c_str());
+// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: Response %s"), response.c_str());
// parsing of reply from Telegram into separate received messages
int i = 0; //messages received counter
if (response != "") {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Sent Update request messages up to %s"), offset.c_str());
+// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Sent Update request messages up to %s"), offset.c_str());
String a = "";
int ch_count = 0;
@@ -213,16 +204,16 @@ void TelegramGetUpdates(String offset) {
}
//check result of parsing process
if (response == "") {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Failed to update"));
+// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Failed to update"));
return;
}
if (0 == i) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: No new messages"));
+// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: No new messages"));
Telegram.message[0][0] = "0";
} else {
Telegram.message[0][0] = String(i); //returns how many messages are in the array
for (int b = 1; b < i+1; b++) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Msg %d %s"), b, Telegram.message[b][0].c_str());
+// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Msg %d %s"), b, Telegram.message[b][0].c_str());
}
TelegramAnalizeMessage();
@@ -248,13 +239,13 @@ void TelegramAnalizeMessage(void) {
Telegram.message[0][1] = id; // Write id of last read message
for (int j = 0; j < 6; j++) {
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: Parsed%d \"%s\""), j, Telegram.message[i][j].c_str());
+// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: Parsed%d \"%s\""), j, Telegram.message[i][j].c_str());
}
}
}
bool TelegramSendMessage(String chat_id, String text) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: sendMessage"));
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: sendMessage"));
if (!TelegramInit()) { return false; }
@@ -264,10 +255,10 @@ bool TelegramSendMessage(String chat_id, String text) {
String command = "bot" + _token + "/sendMessage?chat_id=" + chat_id + "&text=" + text;
String response = TelegramConnectToTelegram(command);
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: Response %s"), response.c_str());
+// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: Response %s"), response.c_str());
if (response.startsWith("{\"ok\":true")) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Message sent"));
+// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Message sent"));
sent = true;
}
@@ -278,7 +269,7 @@ bool TelegramSendMessage(String chat_id, String text) {
/*
void TelegramSendGetMe(void) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: getMe"));
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: getMe"));
if (!TelegramInit()) { return; }
@@ -288,7 +279,7 @@ void TelegramSendGetMe(void) {
// {"ok":true,"result":{"id":1179906608,"is_bot":true,"first_name":"Tasmota","username":"tasmota_bot","can_join_groups":true,"can_read_all_group_messages":false,"supports_inline_queries":false}}
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: Response %s"), response.c_str());
+// AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: Response %s"), response.c_str());
}
*/
From 11d64865347d268a5933c7b645d86f8fa2091e27 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Thu, 11 Jun 2020 19:13:08 +0200
Subject: [PATCH 19/35] Fix time 4 display with SO52 1
---
tasmota/support.ino | 2 +-
tasmota/support_rtc.ino | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/tasmota/support.ino b/tasmota/support.ino
index 8d915cf05..b27cb072d 100644
--- a/tasmota/support.ino
+++ b/tasmota/support.ino
@@ -997,7 +997,7 @@ char* ResponseGetTime(uint32_t format, char* time_str)
snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":%u"), UtcTime());
break;
case 3:
- snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s.%03d\""), GetDateAndTime(DT_LOCAL).c_str(), RtcMillis());
+ snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL_MILLIS).c_str());
break;
default:
snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str());
diff --git a/tasmota/support_rtc.ino b/tasmota/support_rtc.ino
index e918ae99d..445976eb3 100644
--- a/tasmota/support_rtc.ino
+++ b/tasmota/support_rtc.ino
@@ -206,6 +206,14 @@ String GetDateAndTime(uint8_t time_type)
break;
}
String dt = GetDT(time); // 2017-03-07T11:08:02
+
+ if (DT_LOCAL_MILLIS == time_type) {
+ char ms[10];
+ snprintf_P(ms, sizeof(ms), PSTR(".%03d"), RtcMillis());
+ dt += ms;
+ time_type = DT_LOCAL;
+ }
+
if (Settings.flag3.time_append_timezone && (DT_LOCAL == time_type)) { // SetOption52 - Append timezone to JSON time
dt += GetTimeZone(); // 2017-03-07T11:08:02-07:00
}
From 536730273596bb1563c2b6cc8a23c82c4571d91f Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Thu, 11 Jun 2020 19:14:39 +0200
Subject: [PATCH 20/35] Fix compile error
---
tasmota/tasmota.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h
index 49347f1f8..3d3811c89 100644
--- a/tasmota/tasmota.h
+++ b/tasmota/tasmota.h
@@ -214,7 +214,7 @@ enum WeekInMonthOptions {Last, First, Second, Third, Fourth};
enum DayOfTheWeekOptions {Sun=1, Mon, Tue, Wed, Thu, Fri, Sat};
enum MonthNamesOptions {Jan=1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec};
enum HemisphereOptions {North, South};
-enum GetDateAndTimeOptions { DT_LOCAL, DT_UTC, DT_LOCALNOTZ, DT_DST, DT_STD, DT_RESTART, DT_ENERGY, DT_BOOTCOUNT };
+enum GetDateAndTimeOptions { DT_LOCAL, DT_UTC, DT_LOCALNOTZ, DT_DST, DT_STD, DT_RESTART, DT_ENERGY, DT_BOOTCOUNT, DT_LOCAL_MILLIS };
enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE};
From 5427fc937e686f262ee96f2a3d3479cdb6006caf Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Thu, 11 Jun 2020 21:14:30 +0200
Subject: [PATCH 21/35] Fix Dimmer tele inconsistency when SO37 128
---
tasmota/xdrv_04_light.ino | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino
index 1d6ac6a58..a8eaf9629 100644
--- a/tasmota/xdrv_04_light.ino
+++ b/tasmota/xdrv_04_light.ino
@@ -1555,10 +1555,10 @@ void LightState(uint8_t append)
if (!Light.pwm_multi_channels) {
if (unlinked) {
// RGB and W are unlinked, we display the second Power/Dimmer
- ResponseAppend_P(PSTR("\"" D_RSLT_POWER "%d\":\"%s\",\"" D_CMND_DIMMER "%d\":%d"
- ",\"" D_RSLT_POWER "%d\":\"%s\",\"" D_CMND_DIMMER "%d\":%d"),
- Light.device, GetStateText(Light.power & 1), Light.device, light_state.getDimmer(1),
- Light.device + 1, GetStateText(Light.power & 2 ? 1 : 0), Light.device + 1, light_state.getDimmer(2));
+ ResponseAppend_P(PSTR("\"" D_RSLT_POWER "%d\":\"%s\",\"" D_CMND_DIMMER "1\":%d"
+ ",\"" D_RSLT_POWER "%d\":\"%s\",\"" D_CMND_DIMMER "2\":%d"),
+ Light.device, GetStateText(Light.power & 1), light_state.getDimmer(1),
+ Light.device + 1, GetStateText(Light.power & 2 ? 1 : 0), light_state.getDimmer(2));
} else {
GetPowerDevice(scommand, Light.device, sizeof(scommand), Settings.flag.device_index_enable); // SetOption26 - Switch between POWER or POWER1
ResponseAppend_P(PSTR("\"%s\":\"%s\",\"" D_CMND_DIMMER "\":%d"), scommand, GetStateText(Light.power & 1),
From 03cd543127317f3984fea0cdc3149421128824ca Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Fri, 12 Jun 2020 12:38:52 +0200
Subject: [PATCH 22/35] Add support for HP303B
Add support for HP303B Temperature and Pressure sensor by Robert Jaakke (#8638)
---
RELEASENOTES.md | 1 +
tasmota/CHANGELOG.md | 1 +
tasmota/my_user_config.h | 1 +
tasmota/tasmota_configurations.h | 1 +
tasmota/xsns_73_hp303b.ino | 114 ++++++++++++++++---------------
5 files changed, 62 insertions(+), 56 deletions(-)
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index ab01ddaa4..c1637501c 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -77,3 +77,4 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
- Add support for up to eight MCP9808 temperature sensors by device111 (#8594)
- Add support for BL0940 energy monitor as used in Blitzwolf BW-SHP10 (#8175)
- Add initial support for Telegram bot (#8619)
+- Add support for HP303B Temperature and Pressure sensor by Robert Jaakke (#8638)
diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md
index 9033a2bba..0c6ef82bd 100644
--- a/tasmota/CHANGELOG.md
+++ b/tasmota/CHANGELOG.md
@@ -3,6 +3,7 @@
### 8.3.1.3 20200611
- Add initial support for Telegram bot (#8619)
+- Add support for HP303B Temperature and Pressure sensor by Robert Jaakke (#8638)
### 8.3.1.2 20200522
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index ea2e6eefe..d6a140663 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -527,6 +527,7 @@
// #define USE_VEML6075 // [I2cDriver49] Enable VEML6075 UVA/UVB/UVINDEX Sensor (I2C address 0x10) (+2k1 code)
// #define USE_VEML7700 // [I2cDriver50] Enable VEML7700 Ambient Light sensor (I2C addresses 0x10) (+4k5 code)
// #define USE_MCP9808 // [I2cDriver51] Enable MCP9808 temperature sensor (I2C addresses 0x18 - 0x1F) (+0k9 code)
+// #define USE_HP303B // [I2cDriver52] Enable HP303B temperature and pressure sensor (I2C address 0x76 or 0x77) (+6k2 code)
// #define USE_DISPLAY // Add I2C Display Support (+2k code)
#define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0
diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h
index 811076d8f..ff81e486d 100644
--- a/tasmota/tasmota_configurations.h
+++ b/tasmota/tasmota_configurations.h
@@ -155,6 +155,7 @@
//#define USE_TASMOTA_SLAVE // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem)
//#define USE_OPENTHERM // Add support for OpenTherm (+15k code)
//#define USE_MCP9808 // Add support for MCP9808 temperature sensor (+0k9 code)
+//#define USE_HP303B // Add support for HP303B temperature and pressure sensor (I2C address 0x76 or 0x77) (+6k2 code)
#define USE_ENERGY_SENSOR // Add energy sensors (-14k code)
#define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code)
diff --git a/tasmota/xsns_73_hp303b.ino b/tasmota/xsns_73_hp303b.ino
index 1be0a1606..0d9363409 100644
--- a/tasmota/xsns_73_hp303b.ino
+++ b/tasmota/xsns_73_hp303b.ino
@@ -16,58 +16,57 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
+
#ifdef USE_I2C
#ifdef USE_HP303B
/*********************************************************************************************\
- * HP303B - Gas (TVOC - Total Volatile Organic Compounds) and Air Quality (CO2)
+ * HP303B - Pressure and temperature sensor
*
* Source: Lolin LOLIN_HP303B_Library
*
* I2C Address: 0x77 or 0x76
\*********************************************************************************************/
-#define XSNS_73 73
-#define XI2C_52 52 // See I2CDEVICES.md
+#define XSNS_73 73
+#define XI2C_52 52 // See I2CDEVICES.md
+
+#define HP303B_MAX_SENSORS 2
+#define HP303B_START_ADDRESS 0x76
#include
// HP303B Object
LOLIN_HP303B HP303BSensor = LOLIN_HP303B();
-#define HP303B_MAX_SENSORS 2
-#define HP303B_START_ADDRESS 0x76
-
struct {
-char types[7] = "HP303B";
-uint8_t count = 0;
-int16_t oversampling = 7;
+ int16_t oversampling = 7;
+ char types[7] = "HP303B";
+ uint8_t count = 0;
} hp303b_cfg;
struct BHP303B {
- uint8_t address;
- uint8_t valid = 0;
float temperature = NAN;
float pressure = NAN;
+ uint8_t address;
+ uint8_t valid = 0;
} hp303b_sensor[HP303B_MAX_SENSORS];
+
/*********************************************************************************************/
-bool HP303B_Read(uint8_t hp303b_idx)
-{
+bool HP303B_Read(uint32_t hp303b_idx) {
if (hp303b_sensor[hp303b_idx].valid) { hp303b_sensor[hp303b_idx].valid--; }
float t;
+ if (HP303BSensor.measureTempOnce(t, hp303b_sensor[hp303b_idx].address, hp303b_cfg.oversampling) != 0) {
+ return false;
+ }
+
float p;
- int16_t ret;
-
- ret = HP303BSensor.measureTempOnce(t, hp303b_sensor[hp303b_idx].address, hp303b_cfg.oversampling);
- if (ret != 0)
- return false;
-
- ret = HP303BSensor.measurePressureOnce(p, hp303b_sensor[hp303b_idx].address, hp303b_cfg.oversampling);
- if (ret != 0)
+ if (HP303BSensor.measurePressureOnce(p, hp303b_sensor[hp303b_idx].address, hp303b_cfg.oversampling) != 0) {
return false;
+ }
hp303b_sensor[hp303b_idx].temperature = (float)ConvertTemp(t);
- hp303b_sensor[hp303b_idx].pressure = (float)ConvertPressure(p) / 100; //conversion to hPa
+ hp303b_sensor[hp303b_idx].pressure = (float)ConvertPressure(p / 100); // Conversion to hPa
hp303b_sensor[hp303b_idx].valid = SENSOR_MAX_MISS;
return true;
@@ -75,14 +74,11 @@ bool HP303B_Read(uint8_t hp303b_idx)
/********************************************************************************************/
-void HP303B_Detect(void)
-{
- for (uint32_t i = 0; i < HP303B_MAX_SENSORS; i++)
- {
- if (!I2cSetDevice(HP303B_START_ADDRESS + i )) { continue; }
+void HP303B_Detect(void) {
+ for (uint32_t i = 0; i < HP303B_MAX_SENSORS; i++) {
+ if (!I2cSetDevice(HP303B_START_ADDRESS + i)) { continue; }
- if (HP303BSensor.begin(HP303B_START_ADDRESS + i))
- {
+ if (HP303BSensor.begin(HP303B_START_ADDRESS + i)) {
hp303b_sensor[hp303b_cfg.count].address = HP303B_START_ADDRESS + i;
I2cSetActiveFound(hp303b_sensor[hp303b_cfg.count].address, hp303b_cfg.types);
hp303b_cfg.count++;
@@ -90,50 +86,58 @@ void HP303B_Detect(void)
}
}
-void HP303B_EverySecond(void)
-{
+void HP303B_EverySecond(void) {
for (uint32_t i = 0; i < hp303b_cfg.count; i++) {
if (uptime &1) {
- if (!HP303B_Read(i)) {
- AddLogMissed(hp303b_cfg.types, hp303b_sensor[i].valid);
+ if (!HP303B_Read(i)) {
+ AddLogMissed(hp303b_cfg.types, hp303b_sensor[i].valid);
+ }
}
}
- }
}
-void HP303B_Show(bool json)
-{
+void HP303B_Show(bool json) {
for (uint32_t i = 0; i < hp303b_cfg.count; i++) {
- char sensor_name[12];
- strlcpy(sensor_name, hp303b_cfg.types, sizeof(sensor_name));
- if (hp303b_cfg.count > 1) {
- snprintf_P(sensor_name, sizeof(sensor_name), PSTR("%s%c0x%02X"), sensor_name, IndexSeparator(), hp303b_sensor[i].address); // HP303B-0x76, HP303B-0x77
- }
+ if (hp303b_sensor[i].valid) {
+ char sensor_name[12];
+ strlcpy(sensor_name, hp303b_cfg.types, sizeof(sensor_name));
+ if (hp303b_cfg.count > 1) {
+ snprintf_P(sensor_name, sizeof(sensor_name), PSTR("%s%c%02X"), sensor_name, IndexSeparator(), hp303b_sensor[i].address); // HP303B-76, HP303B-77
+ }
+
+ float sealevel = 0.0;
+ if (hp303b_sensor[i].pressure != 0.0) {
+ sealevel = (hp303b_sensor[i].pressure / FastPrecisePow(1.0 - ((float)Settings.altitude / 44330.0), 5.255)) - 21.6;
+ sealevel = ConvertPressure(sealevel);
+ }
- if (hp303b_sensor[i].valid)
- {
char str_temperature[33];
dtostrfd(hp303b_sensor[i].temperature, Settings.flag2.temperature_resolution, str_temperature);
char str_pressure[33];
dtostrfd(hp303b_sensor[i].pressure, Settings.flag2.pressure_resolution, str_pressure);
+ char sea_pressure[33];
+ dtostrfd(sealevel, Settings.flag2.pressure_resolution, sea_pressure);
- if (json)
- {
+ if (json) {
ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_PRESSURE "\":%s"), sensor_name, str_temperature, str_pressure);
+ if (Settings.altitude != 0) {
+ ResponseAppend_P(PSTR(",\"" D_JSON_PRESSUREATSEALEVEL "\":%s"), sea_pressure);
+ }
ResponseJsonEnd();
- #ifdef USE_DOMOTICZ
+#ifdef USE_DOMOTICZ
// Domoticz and knx only support one temp sensor
if ((0 == tele_period) && (0 == i)) {
DomoticzSensor(DZ_TEMP, hp303b_sensor[i].temperature);
}
- #endif // USE_DOMOTICZ
- #ifdef USE_WEBSERVER
- }
- else
- {
+#endif // USE_DOMOTICZ
+#ifdef USE_WEBSERVER
+ } else {
WSContentSend_PD(HTTP_SNS_TEMP, sensor_name, str_temperature, TempUnit());
WSContentSend_PD(HTTP_SNS_PRESSURE, sensor_name, str_pressure, PressureUnit().c_str());
- #endif // USE_WEBSERVER
+ if (Settings.altitude != 0) {
+ WSContentSend_PD(HTTP_SNS_SEAPRESSURE, sensor_name, sea_pressure, PressureUnit().c_str());
+ }
+#endif // USE_WEBSERVER
}
}
}
@@ -152,10 +156,8 @@ bool Xsns73(uint8_t function)
if (FUNC_INIT == function) {
HP303B_Detect();
}
- else if (hp303b_cfg.count)
- {
- switch (function)
- {
+ else if (hp303b_cfg.count) {
+ switch (function) {
case FUNC_EVERY_SECOND:
HP303B_EverySecond();
break;
From a97f391f91491dda1751311a41d3dd0c78d2c3e4 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Fri, 12 Jun 2020 12:40:42 +0200
Subject: [PATCH 23/35] Update I2CDEVICES.md
---
I2CDEVICES.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/I2CDEVICES.md b/I2CDEVICES.md
index 4f6718b90..b67cc9029 100644
--- a/I2CDEVICES.md
+++ b/I2CDEVICES.md
@@ -72,4 +72,5 @@ Index | Define | Driver | Device | Address(es) | Description
48 | USE_AS3935 | xsns_67 | AS3935 | 0x03 | Franklin Lightning Sensor
49 | USE_VEML6075 | xsns_70 | VEML6075 | 0x10 | UVA/UVB/UVINDEX Sensor
50 | USE_VEML7700 | xsns_71 | VEML7700 | 0x10 | Ambient light intensity sensor
- 51 | USE_MCP9808 | xsns_72 | MCP9808 | 0x18 - 0x1F | Temperature sensor
\ No newline at end of file
+ 51 | USE_MCP9808 | xsns_72 | MCP9808 | 0x18 - 0x1F | Temperature sensor
+ 52 | USE_HP303B | xsns_73 | HP303B | 0x76 - 0x77 | Pressure and temperature sensor
\ No newline at end of file
From d64c0a8179500c6a668d4018661e19138496c2b9 Mon Sep 17 00:00:00 2001
From: bovirus <1262554+bovirus@users.noreply.github.com>
Date: Fri, 12 Jun 2020 16:33:19 +0200
Subject: [PATCH 24/35] Update Italian language
---
tasmota/language/it_IT.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h
index f1af3765f..22181a7be 100644
--- a/tasmota/language/it_IT.h
+++ b/tasmota/language/it_IT.h
@@ -672,7 +672,7 @@
#define D_SENSOR_HM10_TX "HM10 - TX"
#define D_SENSOR_LE01MR_RX "LE-01MR - RX"
#define D_SENSOR_LE01MR_TX "LE-01MR - TX"
-#define D_SENSOR_BL0940_RX "BL0940 Rx"
+#define D_SENSOR_BL0940_RX "BL0940 - Rx"
#define D_SENSOR_CC1101_GDO0 "CC1101 - GDO0"
#define D_SENSOR_CC1101_GDO2 "CC1101 - GDO2"
#define D_SENSOR_HRXL_RX "HRXL - RX"
From 0abfcf1954fa6d60fab5a63d04cfde8d4477a32f Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Fri, 12 Jun 2020 16:51:21 +0200
Subject: [PATCH 25/35] Fix global temperature
- Fix global temperature use of float solving intermittend power off (#8175)
- Fix BL0940 power monitoring when powered on but no load present
---
tasmota/support.ino | 6 +++---
tasmota/support_command.ino | 2 +-
tasmota/tasmota.ino | 6 +++---
tasmota/xdrv_03_energy.ino | 7 ++++++-
tasmota/xnrg_14_bl0940.ino | 15 +++++++++------
tasmota/xsns_21_sgp30.ino | 8 ++++----
tasmota/xsns_31_ccs811.ino | 4 +++-
tasmota/xsns_91_prometheus.ino | 2 +-
8 files changed, 30 insertions(+), 20 deletions(-)
diff --git a/tasmota/support.ino b/tasmota/support.ino
index b27cb072d..879a91fb0 100644
--- a/tasmota/support.ino
+++ b/tasmota/support.ino
@@ -687,9 +687,9 @@ void ResetGlobalValues(void)
{
if ((uptime - global_update) > GLOBAL_VALUES_VALID) { // Reset after 5 minutes
global_update = 0;
- global_temperature = 9999;
- global_humidity = 0;
- global_pressure = 0;
+ global_temperature = NAN;
+ global_humidity = 0.0f;
+ global_pressure = 0.0f;
}
}
diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino
index abda76482..fe855ee92 100644
--- a/tasmota/support_command.ino
+++ b/tasmota/support_command.ino
@@ -626,7 +626,7 @@ void CmndGlobalTemp(void)
if (!isnan(temperature) && Settings.flag.temperature_conversion) { // SetOption8 - Switch between Celsius or Fahrenheit
temperature = (temperature - 32) / 1.8; // Celsius
}
- if ((temperature >= -50.0) && (temperature <= 100.0)) {
+ if ((temperature >= -50.0f) && (temperature <= 100.0f)) {
ConvertTemp(temperature);
global_update = 1; // Keep global values just entered valid
}
diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino
index 78c50edc3..7ee0c8316 100644
--- a/tasmota/tasmota.ino
+++ b/tasmota/tasmota.ino
@@ -111,9 +111,9 @@ uint32_t uptime = 0; // Counting every second until 42949
uint32_t loop_load_avg = 0; // Indicative loop load average
uint32_t global_update = 0; // Timestamp of last global temperature and humidity update
uint32_t web_log_index = 1; // Index in Web log buffer (should never be 0)
-float global_temperature = 9999; // Provide a global temperature to be used by some sensors
-float global_humidity = 0; // Provide a global humidity to be used by some sensors
-float global_pressure = 0; // Provide a global pressure to be used by some sensors
+float global_temperature = NAN; // Provide a global temperature to be used by some sensors
+float global_humidity = 0.0f; // Provide a global humidity to be used by some sensors
+float global_pressure = 0.0f; // Provide a global pressure to be used by some sensors
uint16_t tele_period = 9999; // Tele period timer
uint16_t blink_counter = 0; // Number of blink cycles
uint16_t seriallog_timer = 0; // Timer to disable Seriallog
diff --git a/tasmota/xdrv_03_energy.ino b/tasmota/xdrv_03_energy.ino
index f5e4a729d..bf9515b6a 100644
--- a/tasmota/xdrv_03_energy.ino
+++ b/tasmota/xdrv_03_energy.ino
@@ -459,7 +459,12 @@ void EnergyEverySecond(void)
{
// Overtemp check
if (global_update) {
- if (power && (global_temperature != 9999) && (global_temperature > Settings.param[P_OVER_TEMP])) { // Device overtemp, turn off relays
+ if (power && !isnan(global_temperature) && (global_temperature > (float)Settings.param[P_OVER_TEMP])) { // Device overtemp, turn off relays
+
+ char temperature[33];
+ dtostrfd(global_temperature, 1, temperature);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NRG: GlobTemp %s"), temperature);
+
SetAllPower(POWER_ALL_OFF, SRC_OVERTEMP);
}
}
diff --git a/tasmota/xnrg_14_bl0940.ino b/tasmota/xnrg_14_bl0940.ino
index 655f3d8df..bfdbac232 100644
--- a/tasmota/xnrg_14_bl0940.ino
+++ b/tasmota/xnrg_14_bl0940.ino
@@ -83,17 +83,20 @@ void Bl0940Received(void) {
return;
}
- Bl0940.voltage = Bl0940.rx_buffer[12] << 16 | Bl0940.rx_buffer[11] << 8 | Bl0940.rx_buffer[10];
- Bl0940.current = Bl0940.rx_buffer[6] << 16 | Bl0940.rx_buffer[5] << 8 | Bl0940.rx_buffer[4];
- Bl0940.power = Bl0940.rx_buffer[18] << 16 | Bl0940.rx_buffer[17] << 8 | Bl0940.rx_buffer[16];
-// Bl0940.cf_pulses = Bl0940.rx_buffer[24] << 16 | Bl0940.rx_buffer[23] << 8 | Bl0940.rx_buffer[22];
- uint16_t tps1 = Bl0940.rx_buffer[29] << 8 | Bl0940.rx_buffer[28];
+ Bl0940.voltage = Bl0940.rx_buffer[12] << 16 | Bl0940.rx_buffer[11] << 8 | Bl0940.rx_buffer[10]; // V_RMS unsigned
+ Bl0940.current = Bl0940.rx_buffer[6] << 16 | Bl0940.rx_buffer[5] << 8 | Bl0940.rx_buffer[4]; // I_RMS unsigned
+ int32_t power = Bl0940.rx_buffer[18] << 24 | Bl0940.rx_buffer[17] << 16 | Bl0940.rx_buffer[16] << 8; // WATT signed
+ Bl0940.power = abs(power) >> 8; // WATT unsigned
+// Bl0940.cf_pulses = Bl0940.rx_buffer[24] << 16 | Bl0940.rx_buffer[23] << 8 | Bl0940.rx_buffer[22]; // CF_CNT unsigned
+ uint16_t tps1 = Bl0940.rx_buffer[29] << 8 | Bl0940.rx_buffer[28]; // TPS1 unsigned
float t = ((170.0f/448.0f)*(((float)tps1/2.0f)-32.0f))-45.0f;
Bl0940.temperature = ConvertTemp(t);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BL9: U %d, I %d, P %d, T %d"), Bl0940.voltage, Bl0940.current, Bl0940.power, tps1);
+
if (Energy.power_on) { // Powered on
Energy.voltage[0] = (float)Bl0940.voltage / Settings.energy_voltage_calibration;
- if (power != 0) {
+ if (power && (Bl0940.power > Settings.energy_power_calibration)) { // We need at least 1W
Energy.active_power[0] = (float)Bl0940.power / Settings.energy_power_calibration;
Energy.current[0] = (float)Bl0940.current / (Settings.energy_current_calibration * 100);
} else {
diff --git a/tasmota/xsns_21_sgp30.ino b/tasmota/xsns_21_sgp30.ino
index ab417a8c3..ec1e85258 100644
--- a/tasmota/xsns_21_sgp30.ino
+++ b/tasmota/xsns_21_sgp30.ino
@@ -87,7 +87,7 @@ void Sgp30Update(void) // Perform every second to ensure proper operation of th
if (!sgp.IAQmeasure()) {
return; // Measurement failed
}
- if (global_update && (global_humidity > 0) && (global_temperature != 9999)) {
+ if (global_update && (global_humidity > 0) && !isnan(global_temperature)) {
// abs hum in mg/m3
sgp30_abshum=sgp30_AbsoluteHumidity(global_temperature,global_humidity,TempUnit());
sgp.setHumidity(sgp30_abshum*1000);
@@ -118,14 +118,14 @@ void Sgp30Show(bool json)
{
if (sgp30_ready) {
char abs_hum[33];
-
- if (global_update && global_humidity>0 && global_temperature!=9999) {
+
+ if (global_update && (global_humidity > 0) && !isnan(global_temperature)) {
// 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) {
+ if (global_update && global_humidity>0 && !isnan(global_temperature)) {
ResponseAppend_P(PSTR(",\"" D_JSON_AHUM "\":%s"),abs_hum);
}
ResponseJsonEnd();
diff --git a/tasmota/xsns_31_ccs811.ino b/tasmota/xsns_31_ccs811.ino
index a968319cb..aada03d04 100644
--- a/tasmota/xsns_31_ccs811.ino
+++ b/tasmota/xsns_31_ccs811.ino
@@ -65,7 +65,9 @@ void CCS811Update(void) // Perform every n second
TVOC = ccs.getTVOC();
eCO2 = ccs.geteCO2();
CCS811_ready = 1;
- if (global_update && global_humidity>0 && global_temperature!=9999) { ccs.setEnvironmentalData((uint8_t)global_humidity, global_temperature); }
+ if (global_update && (global_humidity > 0) && !isnan(global_temperature)) {
+ ccs.setEnvironmentalData((uint8_t)global_humidity, global_temperature);
+ }
ecnt = 0;
}
} else {
diff --git a/tasmota/xsns_91_prometheus.ino b/tasmota/xsns_91_prometheus.ino
index f5b0eba9d..2a361b550 100644
--- a/tasmota/xsns_91_prometheus.ino
+++ b/tasmota/xsns_91_prometheus.ino
@@ -37,7 +37,7 @@ void HandleMetrics(void)
char parameter[FLOATSZ];
- if (global_temperature != 9999) {
+ if (!isnan(global_temperature)) {
dtostrfd(global_temperature, Settings.flag2.temperature_resolution, parameter);
WSContentSend_P(PSTR("# TYPE global_temperature gauge\nglobal_temperature %s\n"), parameter);
}
From 2b327c96c99448850690ff16fe5126506ee89361 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Fri, 12 Jun 2020 17:42:04 +0200
Subject: [PATCH 26/35] Fix BL0940 invalid overtemp
Fix BL0940 invalid overtemp (#8175)
---
tasmota/xnrg_14_bl0940.ino | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/tasmota/xnrg_14_bl0940.ino b/tasmota/xnrg_14_bl0940.ino
index bfdbac232..61dd8b808 100644
--- a/tasmota/xnrg_14_bl0940.ino
+++ b/tasmota/xnrg_14_bl0940.ino
@@ -60,6 +60,7 @@ struct BL0940 {
float temperature;
int byte_counter = 0;
+ uint16_t tps1 = 0;
uint8_t *rx_buffer = nullptr;
bool received = false;
} Bl0940;
@@ -78,21 +79,25 @@ void Bl0940Received(void) {
// 55 B9 33 00 DE 45 00 94 02 00 CF E4 70 63 02 00 6C 4C 00 13 01 00 09 00 00 00 00 00 E4 01 00 FE 03 00 72
// Hd IFRms--- Current- Reserved Voltage- Reserved Power--- Reserved CF------ Reserved TPS1---- TPS2---- Ck
- if (Bl0940.rx_buffer[0] != BL0940_PACKET_HEADER) {
+ uint16_t tps1 = Bl0940.rx_buffer[29] << 8 | Bl0940.rx_buffer[28]; // TPS1 unsigned
+ if ((Bl0940.rx_buffer[0] != BL0940_PACKET_HEADER) || // Bad header
+ (Bl0940.tps1 && ((tps1 < (Bl0940.tps1 -10)) || (tps1 > (Bl0940.tps1 +10)))) // Invalid temperature change
+ ) {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("BL9: Invalid data"));
return;
}
+ Bl0940.tps1 = tps1;
+ float t = ((170.0f/448.0f)*(((float)Bl0940.tps1/2.0f)-32.0f))-45.0f;
+ Bl0940.temperature = ConvertTemp(t);
+
Bl0940.voltage = Bl0940.rx_buffer[12] << 16 | Bl0940.rx_buffer[11] << 8 | Bl0940.rx_buffer[10]; // V_RMS unsigned
Bl0940.current = Bl0940.rx_buffer[6] << 16 | Bl0940.rx_buffer[5] << 8 | Bl0940.rx_buffer[4]; // I_RMS unsigned
int32_t power = Bl0940.rx_buffer[18] << 24 | Bl0940.rx_buffer[17] << 16 | Bl0940.rx_buffer[16] << 8; // WATT signed
Bl0940.power = abs(power) >> 8; // WATT unsigned
// Bl0940.cf_pulses = Bl0940.rx_buffer[24] << 16 | Bl0940.rx_buffer[23] << 8 | Bl0940.rx_buffer[22]; // CF_CNT unsigned
- uint16_t tps1 = Bl0940.rx_buffer[29] << 8 | Bl0940.rx_buffer[28]; // TPS1 unsigned
- float t = ((170.0f/448.0f)*(((float)tps1/2.0f)-32.0f))-45.0f;
- Bl0940.temperature = ConvertTemp(t);
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BL9: U %d, I %d, P %d, T %d"), Bl0940.voltage, Bl0940.current, Bl0940.power, tps1);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BL9: U %d, I %d, P %d, T %d"), Bl0940.voltage, Bl0940.current, Bl0940.power, Bl0940.tps1);
if (Energy.power_on) { // Powered on
Energy.voltage[0] = (float)Bl0940.voltage / Settings.energy_voltage_calibration;
From 57ffd2715364015fd00c979206017bbe8b75d55a Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Sat, 13 Jun 2020 12:26:55 +0200
Subject: [PATCH 27/35] Alternative method of calculating energy usage
Alternative method of calculating energy usage (#8175)
---
tasmota/xnrg_14_bl0940.ino | 58 ++++++++++++++++++++++++++++++--------
1 file changed, 46 insertions(+), 12 deletions(-)
diff --git a/tasmota/xnrg_14_bl0940.ino b/tasmota/xnrg_14_bl0940.ino
index 61dd8b808..d9bc51db8 100644
--- a/tasmota/xnrg_14_bl0940.ino
+++ b/tasmota/xnrg_14_bl0940.ino
@@ -33,6 +33,8 @@
#define BL0940_UREF 33000
#define BL0940_IREF 2750
+#define BL0940_PULSES_NOT_INITIALIZED -1
+
#define BL0940_BUFFER_SIZE 36
#define BL0940_WRITE_COMMAND 0xA0 // 0xA8 according to documentation
@@ -55,8 +57,11 @@ struct BL0940 {
long voltage = 0;
long current = 0;
long power = 0;
-// long power_cycle_first = 0;
-// long cf_pulses = 0;
+
+ long power_cycle_first = 0;
+ long cf_pulses = 0;
+ long cf_pulses_last_time = BL0940_PULSES_NOT_INITIALIZED;
+
float temperature;
int byte_counter = 0;
@@ -79,9 +84,9 @@ void Bl0940Received(void) {
// 55 B9 33 00 DE 45 00 94 02 00 CF E4 70 63 02 00 6C 4C 00 13 01 00 09 00 00 00 00 00 E4 01 00 FE 03 00 72
// Hd IFRms--- Current- Reserved Voltage- Reserved Power--- Reserved CF------ Reserved TPS1---- TPS2---- Ck
- uint16_t tps1 = Bl0940.rx_buffer[29] << 8 | Bl0940.rx_buffer[28]; // TPS1 unsigned
- if ((Bl0940.rx_buffer[0] != BL0940_PACKET_HEADER) || // Bad header
- (Bl0940.tps1 && ((tps1 < (Bl0940.tps1 -10)) || (tps1 > (Bl0940.tps1 +10)))) // Invalid temperature change
+ uint16_t tps1 = Bl0940.rx_buffer[29] << 8 | Bl0940.rx_buffer[28]; // TPS1 unsigned
+ if ((Bl0940.rx_buffer[0] != BL0940_PACKET_HEADER) || // Bad header
+ (Bl0940.tps1 && ((tps1 < (Bl0940.tps1 -10)) || (tps1 > (Bl0940.tps1 +10)))) // Invalid temperature change
) {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("BL9: Invalid data"));
return;
@@ -91,17 +96,19 @@ void Bl0940Received(void) {
float t = ((170.0f/448.0f)*(((float)Bl0940.tps1/2.0f)-32.0f))-45.0f;
Bl0940.temperature = ConvertTemp(t);
- Bl0940.voltage = Bl0940.rx_buffer[12] << 16 | Bl0940.rx_buffer[11] << 8 | Bl0940.rx_buffer[10]; // V_RMS unsigned
- Bl0940.current = Bl0940.rx_buffer[6] << 16 | Bl0940.rx_buffer[5] << 8 | Bl0940.rx_buffer[4]; // I_RMS unsigned
- int32_t power = Bl0940.rx_buffer[18] << 24 | Bl0940.rx_buffer[17] << 16 | Bl0940.rx_buffer[16] << 8; // WATT signed
- Bl0940.power = abs(power) >> 8; // WATT unsigned
-// Bl0940.cf_pulses = Bl0940.rx_buffer[24] << 16 | Bl0940.rx_buffer[23] << 8 | Bl0940.rx_buffer[22]; // CF_CNT unsigned
+ Bl0940.voltage = Bl0940.rx_buffer[12] << 16 | Bl0940.rx_buffer[11] << 8 | Bl0940.rx_buffer[10]; // V_RMS unsigned
+ Bl0940.current = Bl0940.rx_buffer[6] << 16 | Bl0940.rx_buffer[5] << 8 | Bl0940.rx_buffer[4]; // I_RMS unsigned
+ int32_t power = Bl0940.rx_buffer[18] << 24 | Bl0940.rx_buffer[17] << 16 | Bl0940.rx_buffer[16] << 8; // WATT signed
+ Bl0940.power = abs(power) >> 8; // WATT unsigned
+ int32_t cf_cnt = Bl0940.rx_buffer[24] << 24 | Bl0940.rx_buffer[23] << 16 | Bl0940.rx_buffer[22] << 8; // CF_CNT signed
+ Bl0940.cf_pulses = abs(cf_cnt) >> 8;
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BL9: U %d, I %d, P %d, T %d"), Bl0940.voltage, Bl0940.current, Bl0940.power, Bl0940.tps1);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BL9: U %d, I %d, P %d, C %d, T %d"),
+ Bl0940.voltage, Bl0940.current, Bl0940.power, Bl0940.cf_pulses, Bl0940.tps1);
if (Energy.power_on) { // Powered on
Energy.voltage[0] = (float)Bl0940.voltage / Settings.energy_voltage_calibration;
- if (power && (Bl0940.power > Settings.energy_power_calibration)) { // We need at least 1W
+ if (power && (Bl0940.power > Settings.energy_power_calibration)) { // We need at least 1W
Energy.active_power[0] = (float)Bl0940.power / Settings.energy_power_calibration;
Energy.current[0] = (float)Bl0940.current / (Settings.energy_current_calibration * 100);
} else {
@@ -163,10 +170,37 @@ void Bl0940EverySecond(void) {
Bl0940.current = 0;
Bl0940.power = 0;
} else {
+/*
+ // Calculate energy by using active power
if (Energy.active_power[0]) {
Energy.kWhtoday_delta += (Energy.active_power[0] * 1000) / 36;
EnergyUpdateToday();
}
+*/
+ // Calculate energy by using active energy pulse count
+ if (BL0940_PULSES_NOT_INITIALIZED == Bl0940.cf_pulses_last_time) {
+ Bl0940.cf_pulses_last_time = Bl0940.cf_pulses; // Init after restart
+ } else {
+ uint32_t cf_pulses = 0;
+ if (Bl0940.cf_pulses < Bl0940.cf_pulses_last_time) { // Rolled over after 0xFFFFFF (16777215) pulses
+ cf_pulses = (0x1000000 - Bl0940.cf_pulses_last_time) + Bl0940.cf_pulses;
+ } else {
+ cf_pulses = Bl0940.cf_pulses - Bl0940.cf_pulses_last_time;
+ }
+ if (cf_pulses && Energy.active_power[0]) {
+ if (cf_pulses < 16) { // max load for SHP10: 4.00kW (3.68kW)
+ uint32_t watt256 = (1638400 * 256) / Settings.energy_power_calibration;
+ uint32_t delta = (cf_pulses * watt256) / 36;
+ Bl0940.cf_pulses_last_time = Bl0940.cf_pulses;
+ Energy.kWhtoday_delta += delta;
+ } else {
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR("BL9: Overload"));
+ Bl0940.cf_pulses_last_time = BL0940_PULSES_NOT_INITIALIZED;
+ }
+ EnergyUpdateToday();
+ }
+ }
+
}
Bl0940Serial->flush();
From fcc0a29909779ab492a7c1ac34ac37dba1351e3b Mon Sep 17 00:00:00 2001
From: gemu2015
Date: Sat, 13 Jun 2020 13:05:25 +0200
Subject: [PATCH 28/35] scripter support for global vars
---
tasmota/xdrv_10_scripter.ino | 247 +++++++++++++++++++++++++++++++++--
1 file changed, 236 insertions(+), 11 deletions(-)
diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino
index 4d0bc1588..35427e67a 100755
--- a/tasmota/xdrv_10_scripter.ino
+++ b/tasmota/xdrv_10_scripter.ino
@@ -26,7 +26,6 @@ uses about 17 k of flash
to do
optimize code for space
-g:var gloabal vars (via udp broadcast)
remarks
@@ -237,7 +236,11 @@ extern VButton *buttons[MAXBUTTONS];
#endif
typedef union {
+#ifdef USE_SCRIPT_GLOBVARS
+ uint16_t data;
+#else
uint8_t data;
+#endif
struct {
uint8_t is_string : 1; // string or number
uint8_t is_permanent : 1;
@@ -247,6 +250,9 @@ typedef union {
uint8_t settable : 1;
uint8_t is_filter : 1;
uint8_t constant : 1;
+#ifdef USE_SCRIPT_GLOBVARS
+ uint8_t global : 1;
+#endif
};
} SCRIPT_TYPE;
@@ -276,6 +282,32 @@ typedef union {
};
} FILE_FLAGS;
+typedef union {
+ uint8_t data;
+ struct {
+ uint8_t nutu8 : 1;
+ uint8_t nutu7 : 1;
+ uint8_t nutu6 : 1;
+ uint8_t nutu5 : 1;
+ uint8_t nutu4 : 1;
+ uint8_t nutu3 : 1;
+ uint8_t udp_connected : 1;
+ uint8_t udp_used : 1;
+ };
+} UDP_FLAGS;
+
+
+#define NUM_RES 0xfe
+#define STR_RES 0xfd
+#define VAR_NV 0xff
+
+#define NTYPE 0
+#define STYPE 0x80
+
+#ifndef FLT_MAX
+#define FLT_MAX 99999999
+#endif
+
#define SFS_MAX 4
// global memory
struct SCRIPT_MEM {
@@ -308,12 +340,19 @@ struct SCRIPT_MEM {
uint8_t script_sd_found;
char flink[2][14];
#endif
+#ifdef USE_SCRIPT_GLOBVARS
+ UDP_FLAGS udp_flags;
+#endif
} glob_script_mem;
+#ifdef USE_SCRIPT_GLOBVARS
+IPAddress last_udp_ip;
+#endif
int16_t last_findex;
uint8_t tasm_cmd_activ=0;
uint8_t fast_script=0;
+uint8_t glob_script=0;
uint32_t script_lastmillis;
@@ -436,6 +475,16 @@ char *script;
} else {
vtypes[vars].bits.is_autoinc=0;
}
+
+#ifdef USE_SCRIPT_GLOBVARS
+ if (*lp=='g' && *(lp+1)==':') {
+ lp+=2;
+ vtypes[vars].bits.global=1;
+ glob_script_mem.udp_flags.udp_used = 1;
+ } else {
+ vtypes[vars].bits.global=0;
+ }
+#endif
if ((*lp=='m' || *lp=='M') && *(lp+1)==':') {
uint8_t flg=*lp;
lp+=2;
@@ -703,10 +752,112 @@ char *script;
// store start of actual program here
glob_script_mem.scriptptr=lp-1;
glob_script_mem.scriptptr_bu=glob_script_mem.scriptptr;
+
+#ifdef USE_SCRIPT_GLOBVARS
+ if (glob_script_mem.udp_flags.udp_used) {
+ Script_Init_UDP();
+ glob_script=Run_Scripter(">G",-2,0);
+ }
+#endif
+
return 0;
}
+#ifdef USE_SCRIPT_GLOBVARS
+#define SCRIPT_UDP_BUFFER_SIZE 128
+#define SCRIPT_UDP_PORT 1999
+IPAddress script_udp_remote_ip;
+
+void Script_Init_UDP() {
+ if (global_state.wifi_down) return;
+ if (glob_script_mem.udp_flags.udp_connected) return;
+
+ if (PortUdp.beginMulticast(WiFi.localIP(), IPAddress(239,255,255,250), SCRIPT_UDP_PORT)) {
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP "SCRIPT UDP started"));
+ glob_script_mem.udp_flags.udp_connected = 1;
+ } else {
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP "SCRIPT UDP failed"));
+ glob_script_mem.udp_flags.udp_connected = 0;
+ }
+}
+void Script_PollUdp(void) {
+ if (!glob_script_mem.udp_flags.udp_used) return;
+ if (glob_script_mem.udp_flags.udp_connected ) {
+ while (PortUdp.parsePacket()) {
+ char packet_buffer[SCRIPT_UDP_BUFFER_SIZE];
+ int32_t len = PortUdp.read(packet_buffer, SCRIPT_UDP_BUFFER_SIZE -1);
+ packet_buffer[len] = 0;
+ script_udp_remote_ip = PortUdp.remoteIP();
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("UDP: Packet %s - %d - %s"), packet_buffer, len, script_udp_remote_ip.toString().c_str());
+ char *lp=packet_buffer;
+ if (!strncmp(lp,"=>",2)) {
+ lp+=2;
+ char *cp=strchr(lp,'=');
+ if (cp) {
+ char vnam[32];
+ for (uint32_t count=0; countG",2,0);
+ }
+ }
+ }
+ }
+ optimistic_yield(100);
+ }
+ } else {
+ Script_Init_UDP();
+ }
+}
+
+void script_udp_sendvar(char *vname,float *fp,char *sp) {
+ if (!glob_script_mem.udp_flags.udp_used) return;
+ if (!glob_script_mem.udp_flags.udp_connected) return;
+
+ char sbuf[SCRIPT_MAXSSIZE+4];
+ strcpy(sbuf,"=>");
+ strcat(sbuf,vname);
+ strcat(sbuf,"=");
+ if (fp) {
+ char flstr[16];
+ dtostrfd(*fp,8,flstr);
+ strcat(sbuf,flstr);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("num var updated - %s"),sbuf);
+ } else {
+ strcat(sbuf,sp);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("string var updated - %s"),sbuf);
+ }
+ PortUdp.beginPacket(IPAddress(239,255,255,250), SCRIPT_UDP_PORT);
+ // Udp.print(String("RET UC: ") + String(recv_Packet));
+ PortUdp.write((const uint8_t*)sbuf,strlen(sbuf));
+ PortUdp.endPacket();
+}
+
+#endif
+
#ifdef USE_LIGHT
#ifdef USE_WS2812
void ws2812_set_array(float *array ,uint32_t len, uint32_t offset) {
@@ -723,16 +874,7 @@ void ws2812_set_array(float *array ,uint32_t len, uint32_t offset) {
#endif
#endif
-#define NUM_RES 0xfe
-#define STR_RES 0xfd
-#define VAR_NV 0xff
-#define NTYPE 0
-#define STYPE 0x80
-
-#ifndef FLT_MAX
-#define FLT_MAX 99999999
-#endif
float median_array(float *array,uint8_t len) {
uint8_t ind[len];
@@ -1011,6 +1153,37 @@ uint32_t MeasurePulseTime(int32_t in) {
}
#endif // USE_ANGLE_FUNC
+#ifdef USE_SCRIPT_GLOBVARS
+uint32_t match_vars(char *dvnam, float **fp, char **sp, uint32_t *ind) {
+ uint16_t olen=strlen(dvnam);
+ struct T_INDEX *vtp=glob_script_mem.type;
+ for (uint32_t count=0; count ff=nothing found, fe=constant number,fd = constant string else bit 7 => 80 = string, 0 = number
// no flash strings here for performance reasons!!!
char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,JsonObject *jo) {
@@ -1765,12 +1938,40 @@ chknext:
len=0;
goto exit;
}
+ if (!strncmp(vname,"is(",3)) {
+ lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0);
+ SCRIPT_SKIP_SPACES
+ if (*lp!='"') {
+ break;
+ }
+ lp++;
+ char *sstr=lp;
+ for (uint32_t cnt=0; cnt<256; cnt++) {
+ if (lp[cnt]='\n' || lp[cnt]=='"') {
+ lp+=cnt+1;
+ break;
+ }
+ }
+ char str[SCRIPT_MAXSSIZE];
+ GetTextIndexed(str, sizeof(str), fvar, sstr);
+ lp++;
+ if (sp) strlcpy(sp,str,glob_script_mem.max_ssize);
+ fvar=0;
+ len=0;
+ goto exit;
+ }
break;
case 'l':
if (!strncmp(vname,"lip",3)) {
if (sp) strlcpy(sp,(const char*)WiFi.localIP().toString().c_str(),glob_script_mem.max_ssize);
goto strexit;
}
+#ifdef USE_SCRIPT_GLOBVARS
+ if (!strncmp(vname,"luip",4)) {
+ if (sp) strlcpy(sp,IPAddressToString(last_udp_ip),glob_script_mem.max_ssize);
+ goto strexit;
+ }
+#endif
if (!strncmp(vname,"loglvl",6)) {
fvar=glob_script_mem.script_loglevel;
tind->index=SCRIPT_LOGLEVEL;
@@ -3167,7 +3368,7 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) {
if (!if_exe[ifstck] && if_state[ifstck]!=1) goto next_line;
#ifdef IFTHEN_DEBUG
- sprintf(tbuff,"stack=%d,exe=%d,state=%d,cmpres=%d execute line: ",ifstck,if_exe[ifstck],if_state[ifstck],if_result[ifstck]);
+ sdtoff(tbuff,"stack=%d,exe=%d,state=%d,cmpres=%d execute line: ",ifstck,if_exe[ifstck],if_state[ifstck],if_result[ifstck]);
toLogEOL(tbuff,lp);
#endif
@@ -3382,8 +3583,16 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) {
}
goto next_line;
} else {
+ char *vnp=lp;
lp=isvar(lp,&vtype,&ind,&sysvar,0,0);
if (vtype!=VAR_NV) {
+#ifdef USE_SCRIPT_GLOBVARS
+ char varname[16];
+ uint32_t vnl=(uint32_t)lp-(uint32)vnp;
+ strncpy(varname,vnp,vnl);
+ varname[vnl]=0;
+#endif
+
// found variable as result
globvindex=ind.index; // save destination var index here
globaindex=last_findex;
@@ -3451,6 +3660,11 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) {
}
// var was changed
glob_script_mem.type[globvindex].bits.changed=1;
+#ifdef USE_SCRIPT_GLOBVARS
+ if (glob_script_mem.type[globvindex].bits.global) {
+ script_udp_sendvar(varname,dfvar,0);
+ }
+#endif
if (glob_script_mem.type[globvindex].bits.is_filter) {
if (globaindex>=0) {
Set_MFVal(glob_script_mem.type[globvindex].index,globaindex,*dfvar);
@@ -3492,6 +3706,11 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) {
if (!glob_script_mem.var_not_found) {
// var was changed
glob_script_mem.type[globvindex].bits.changed=1;
+#ifdef USE_SCRIPT_GLOBVARS
+ if (glob_script_mem.type[globvindex].bits.global) {
+ script_udp_sendvar(varname,0,str);
+ }
+#endif
if (lastop==OPER_EQU) {
strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize);
} else if (lastop==OPER_PLSEQU) {
@@ -6136,6 +6355,12 @@ bool Xdrv10(uint8_t function)
break;
#endif
+#ifdef USE_SCRIPT_GLOBVARS
+ case FUNC_LOOP:
+ Script_PollUdp();
+ break;
+#endif
+
}
return result;
}
From 1e577a8213d6e7724624dcdbd41183d56e919a2b Mon Sep 17 00:00:00 2001
From: device111 <48546979+device111@users.noreply.github.com>
Date: Sat, 13 Jun 2020 15:08:52 +0200
Subject: [PATCH 29/35] Fix VEML7700 lux measurement, add new command
---
tasmota/xsns_71_veml7700.ino | 32 +++++++++++++++++++-------------
1 file changed, 19 insertions(+), 13 deletions(-)
diff --git a/tasmota/xsns_71_veml7700.ino b/tasmota/xsns_71_veml7700.ino
index 3869e72d6..695e35824 100644
--- a/tasmota/xsns_71_veml7700.ino
+++ b/tasmota/xsns_71_veml7700.ino
@@ -40,27 +40,27 @@ const char JSON_SNS_VEML7700[] PROGMEM = ",\"%s\":{\"" D_JSON_ILLUMINANCE "\":%d
#define D_CMND_VEML7700_PWR "power"
#define D_CMND_VEML7700_GAIN "gain"
#define D_CMND_VEML7700_INTTIME "inttime"
+#define D_CMND_VEML7700_PERSIST "persist"
const char S_JSON_VEML7700_COMMAND_NVALUE[] PROGMEM = "{\"" D_NAME_VEML7700 "\":{\"%s\":%d}}";
-const char kVEML7700_Commands[] PROGMEM = D_CMND_VEML7700_PWR "|" D_CMND_VEML7700_GAIN "|" D_CMND_VEML7700_INTTIME;
+const char kVEML7700_Commands[] PROGMEM = D_CMND_VEML7700_PWR "|" D_CMND_VEML7700_GAIN "|" D_CMND_VEML7700_INTTIME "|" D_CMND_VEML7700_PERSIST;
enum VEML7700_Commands { // commands for Console
CMND_VEML7700_PWR,
CMND_VEML7700_GAIN,
CMND_VEML7700_SET_IT,
- };
+ CMND_VEML7700_PERSIST,
+};
struct VEML7700STRUCT
{
+ bool active = 0;
char types[9] = D_NAME_VEML7700;
uint8_t address = VEML7700_I2CADDR_DEFAULT;
- //uint16_t lux = 0;
- //uint16_t white = 0;
- uint16_t lux_normalized = 0;
- uint16_t white_normalized = 0;
+ uint32_t lux_normalized = 0;
+ uint32_t white_normalized = 0;
} veml7700_sensor;
-uint8_t veml7700_active = 0;
/********************************************************************************************/
@@ -68,7 +68,7 @@ void VEML7700Detect(void) {
if (!I2cSetDevice(veml7700_sensor.address)) return;
if (veml7700.begin()) {
I2cSetActiveFound(veml7700_sensor.address, veml7700_sensor.types);
- veml7700_active = 1;
+ veml7700_sensor.active = 1;
}
}
@@ -97,10 +97,8 @@ uint8_t VEML7700TranslateItInt (uint16_t ittimems){
}
void VEML7700EverySecond(void) {
- veml7700_sensor.lux_normalized = (uint16_t) veml7700.readLuxNormalized();
- veml7700_sensor.white_normalized = (uint16_t) veml7700.readWhiteNormalized();
- //veml7700_sensor.lux = (uint16_t) veml7700.readLux();
- //veml7700_sensor.white = (uint16_t) veml7700.readWhite();
+ veml7700_sensor.lux_normalized = (uint32_t) veml7700.readLuxNormalized();
+ veml7700_sensor.white_normalized = (uint32_t) veml7700.readWhiteNormalized();
}
void VEML7700Show(bool json)
@@ -152,6 +150,14 @@ bool VEML7700Cmd(void) {
Response_P(S_JSON_VEML7700_COMMAND_NVALUE, command, dataret);
}
break;
+ case CMND_VEML7700_PERSIST:
+ if (XdrvMailbox.data_len) {
+ if (4 >= XdrvMailbox.payload) {
+ veml7700.setPersistence(XdrvMailbox.payload);
+ }
+ }
+ Response_P(S_JSON_VEML7700_COMMAND_NVALUE, command, veml7700.getPersistence());
+ break;
default:
return false;
}
@@ -174,7 +180,7 @@ bool Xsns71(uint8_t function)
if (FUNC_INIT == function) {
VEML7700Detect();
}
- else if (veml7700_active) {
+ else if (veml7700_sensor.active) {
switch (function) {
case FUNC_EVERY_SECOND:
VEML7700EverySecond();
From f46d3751a01a112075e98ae5574a6b6a4ec3e1fc Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Sat, 13 Jun 2020 15:10:12 +0200
Subject: [PATCH 30/35] Refactor some energy monitoring
---
tasmota/xdrv_03_energy.ino | 3 +++
tasmota/xnrg_02_cse7766.ino | 45 +++++++++++++++----------------------
tasmota/xnrg_14_bl0940.ino | 27 ++++++++++------------
3 files changed, 33 insertions(+), 42 deletions(-)
diff --git a/tasmota/xdrv_03_energy.ino b/tasmota/xdrv_03_energy.ino
index bf9515b6a..b84d69407 100644
--- a/tasmota/xdrv_03_energy.ino
+++ b/tasmota/xdrv_03_energy.ino
@@ -1142,6 +1142,9 @@ bool Xdrv03(uint8_t function)
case FUNC_EVERY_250_MSECOND:
XnrgCall(FUNC_EVERY_250_MSECOND);
break;
+ case FUNC_EVERY_SECOND:
+ XnrgCall(FUNC_EVERY_SECOND);
+ break;
case FUNC_SERIAL:
result = XnrgCall(FUNC_SERIAL);
break;
diff --git a/tasmota/xnrg_02_cse7766.ino b/tasmota/xnrg_02_cse7766.ino
index f0aa210d1..9c8417bc6 100644
--- a/tasmota/xnrg_02_cse7766.ino
+++ b/tasmota/xnrg_02_cse7766.ino
@@ -59,8 +59,7 @@ struct CSE {
bool received = false;
} Cse;
-void CseReceived(void)
-{
+void CseReceived(void) {
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// F2 5A 02 F7 60 00 03 61 00 40 10 05 72 40 51 A6 58 63 10 1B E1 7F 4D 4E - F2 = Power cycle exceeds range - takes too long - No load
// 55 5A 02 F7 60 00 03 5A 00 40 10 04 8B 9F 51 A6 58 18 72 75 61 AC A1 30 - 55 = Ok, 61 = Power not valid (load below 5W)
@@ -69,7 +68,7 @@ void CseReceived(void)
uint8_t header = Cse.rx_buffer[0];
if ((header & 0xFC) == 0xFC) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Abnormal hardware"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CSE: Abnormal hardware"));
return;
}
@@ -142,8 +141,7 @@ void CseReceived(void)
}
}
-bool CseSerialInput(void)
-{
+bool CseSerialInput(void) {
while (CseSerial->available()) {
yield();
uint8_t serial_in_byte = CseSerial->read();
@@ -162,12 +160,12 @@ bool CseSerialInput(void)
Cse.received = false;
return true;
} else {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: " D_CHECKSUM_FAILURE));
do { // Sync buffer with data (issue #1907 and #3425)
memmove(Cse.rx_buffer, Cse.rx_buffer +1, 24);
Cse.byte_counter--;
} while ((Cse.byte_counter > 2) && (0x5A != Cse.rx_buffer[1]));
if (0x5A != Cse.rx_buffer[1]) {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CSE: " D_CHECKSUM_FAILURE));
Cse.received = false;
Cse.byte_counter = 0;
}
@@ -186,34 +184,31 @@ bool CseSerialInput(void)
/********************************************************************************************/
-void CseEverySecond(void)
-{
+void CseEverySecond(void) {
if (Energy.data_valid[0] > ENERGY_WATCHDOG) {
Cse.voltage_cycle = 0;
Cse.current_cycle = 0;
Cse.power_cycle = 0;
} else {
- long cf_frequency = 0;
-
if (CSE_PULSES_NOT_INITIALIZED == Cse.cf_pulses_last_time) {
Cse.cf_pulses_last_time = Cse.cf_pulses; // Init after restart
} else {
- if (Cse.cf_pulses < Cse.cf_pulses_last_time) { // Rolled over after 65535 pulses
- cf_frequency = (65536 - Cse.cf_pulses_last_time) + Cse.cf_pulses;
+ uint32_t cf_pulses = 0;
+ if (Cse.cf_pulses < Cse.cf_pulses_last_time) { // Rolled over after 0xFFFF (65535) pulses
+ cf_pulses = (0x10000 - Cse.cf_pulses_last_time) + Cse.cf_pulses;
} else {
- cf_frequency = Cse.cf_pulses - Cse.cf_pulses_last_time;
+ cf_pulses = Cse.cf_pulses - Cse.cf_pulses_last_time;
}
- if (cf_frequency && Energy.active_power[0]) {
- unsigned long delta = (cf_frequency * Settings.energy_power_calibration) / 36;
+ if (cf_pulses && Energy.active_power[0]) {
+ uint32_t delta = (cf_pulses * Settings.energy_power_calibration) / 36;
// prevent invalid load delta steps even checksum is valid (issue #5789):
-// if (delta <= (3680*100/36) * 10 ) { // max load for S31/Pow R2: 3.68kW
// prevent invalid load delta steps even checksum is valid but allow up to 4kW (issue #7155):
- if (delta <= (4000*100/36) * 10 ) { // max load for S31/Pow R2: 4.00kW
+ if (delta <= (4000 * 1000 / 36)) { // max load for S31/Pow R2: 4.00kW
Cse.cf_pulses_last_time = Cse.cf_pulses;
Energy.kWhtoday_delta += delta;
}
else {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Load overflow"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CSE: Overload"));
Cse.cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED;
}
EnergyUpdateToday();
@@ -222,8 +217,7 @@ void CseEverySecond(void)
}
}
-void CseSnsInit(void)
-{
+void CseSnsInit(void) {
// Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions
// CseSerial = new TasmotaSerial(Pin(GPIO_CSE7766_RX), Pin(GPIO_CSE7766_TX), 1);
CseSerial = new TasmotaSerial(Pin(GPIO_CSE7766_RX), -1, 1);
@@ -241,8 +235,7 @@ void CseSnsInit(void)
}
}
-void CseDrvInit(void)
-{
+void CseDrvInit(void) {
// if (PinUsed(GPIO_CSE7766_RX) && PinUsed(GPIO_CSE7766_TX)) {
if (PinUsed(GPIO_CSE7766_RX)) {
Cse.rx_buffer = (uint8_t*)(malloc(CSE_BUFFER_SIZE));
@@ -252,8 +245,7 @@ void CseDrvInit(void)
}
}
-bool CseCommand(void)
-{
+bool CseCommand(void) {
bool serviced = true;
if (CMND_POWERSET == Energy.command_code) {
@@ -280,15 +272,14 @@ bool CseCommand(void)
* Interface
\*********************************************************************************************/
-bool Xnrg02(uint8_t function)
-{
+bool Xnrg02(uint8_t function) {
bool result = false;
switch (function) {
case FUNC_LOOP:
if (CseSerial) { CseSerialInput(); }
break;
- case FUNC_ENERGY_EVERY_SECOND:
+ case FUNC_EVERY_SECOND:
CseEverySecond();
break;
case FUNC_COMMAND:
diff --git a/tasmota/xnrg_14_bl0940.ino b/tasmota/xnrg_14_bl0940.ino
index d9bc51db8..fae36ea3a 100644
--- a/tasmota/xnrg_14_bl0940.ino
+++ b/tasmota/xnrg_14_bl0940.ino
@@ -57,11 +57,9 @@ struct BL0940 {
long voltage = 0;
long current = 0;
long power = 0;
-
long power_cycle_first = 0;
long cf_pulses = 0;
long cf_pulses_last_time = BL0940_PULSES_NOT_INITIALIZED;
-
float temperature;
int byte_counter = 0;
@@ -88,7 +86,7 @@ void Bl0940Received(void) {
if ((Bl0940.rx_buffer[0] != BL0940_PACKET_HEADER) || // Bad header
(Bl0940.tps1 && ((tps1 < (Bl0940.tps1 -10)) || (tps1 > (Bl0940.tps1 +10)))) // Invalid temperature change
) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("BL9: Invalid data"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BL9: Invalid data"));
return;
}
@@ -146,13 +144,12 @@ bool Bl0940SerialInput(void) {
Bl0940.received = false;
return true;
} else {
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("BL9: " D_CHECKSUM_FAILURE));
do { // Sync buffer with data (issue #1907 and #3425)
memmove(Bl0940.rx_buffer, Bl0940.rx_buffer +1, BL0940_BUFFER_SIZE -1);
Bl0940.byte_counter--;
} while ((Bl0940.byte_counter > 1) && (BL0940_PACKET_HEADER != Bl0940.rx_buffer[0]));
if (BL0940_PACKET_HEADER != Bl0940.rx_buffer[0]) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("BL9: " D_CHECKSUM_FAILURE));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BL9: " D_CHECKSUM_FAILURE));
Bl0940.received = false;
Bl0940.byte_counter = 0;
}
@@ -188,13 +185,13 @@ void Bl0940EverySecond(void) {
cf_pulses = Bl0940.cf_pulses - Bl0940.cf_pulses_last_time;
}
if (cf_pulses && Energy.active_power[0]) {
- if (cf_pulses < 16) { // max load for SHP10: 4.00kW (3.68kW)
- uint32_t watt256 = (1638400 * 256) / Settings.energy_power_calibration;
- uint32_t delta = (cf_pulses * watt256) / 36;
+ uint32_t watt256 = (1638400 * 256) / Settings.energy_power_calibration;
+ uint32_t delta = (cf_pulses * watt256) / 36;
+ if (delta <= (4000 * 1000 / 36)) { // max load for SHP10: 4.00kW (3.68kW)
Bl0940.cf_pulses_last_time = Bl0940.cf_pulses;
Energy.kWhtoday_delta += delta;
} else {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("BL9: Overload"));
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BL9: Overload"));
Bl0940.cf_pulses_last_time = BL0940_PULSES_NOT_INITIALIZED;
}
EnergyUpdateToday();
@@ -203,6 +200,8 @@ void Bl0940EverySecond(void) {
}
+// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BL9: Poll"));
+
Bl0940Serial->flush();
Bl0940Serial->write(BL0940_READ_COMMAND);
Bl0940Serial->write(BL0940_FULL_PACKET);
@@ -211,7 +210,7 @@ void Bl0940EverySecond(void) {
void Bl0940SnsInit(void) {
// Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions
Bl0940Serial = new TasmotaSerial(Pin(GPIO_BL0940_RX), Pin(GPIO_TXD), 1);
- if (Bl0940Serial->begin(4800, 2)) {
+ if (Bl0940Serial->begin(4800, 1)) {
if (Bl0940Serial->hardwareSerial()) {
ClaimSerial();
}
@@ -268,8 +267,7 @@ bool Bl0940Command(void) {
return serviced;
}
-void Bl0940Show(bool json)
-{
+void Bl0940Show(bool json) {
char temperature[33];
dtostrfd(Bl0940.temperature, Settings.flag2.temperature_resolution, temperature);
@@ -294,15 +292,14 @@ void Bl0940Show(bool json)
* Interface
\*********************************************************************************************/
-bool Xnrg14(uint8_t function)
-{
+bool Xnrg14(uint8_t function) {
bool result = false;
switch (function) {
case FUNC_LOOP:
if (Bl0940Serial) { Bl0940SerialInput(); }
break;
- case FUNC_ENERGY_EVERY_SECOND:
+ case FUNC_EVERY_SECOND:
Bl0940EverySecond();
break;
case FUNC_JSON_APPEND:
From 4f108e05045d9a88685647cbdf2e3e56fcb58b51 Mon Sep 17 00:00:00 2001
From: gemu2015
Date: Sun, 14 Jun 2020 12:07:12 +0200
Subject: [PATCH 31/35] fix sml modbus raw mode
---
tasmota/xsns_53_sml.ino | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/tasmota/xsns_53_sml.ino b/tasmota/xsns_53_sml.ino
index cb51bcc82..18fd53bf5 100755
--- a/tasmota/xsns_53_sml.ino
+++ b/tasmota/xsns_53_sml.ino
@@ -2405,12 +2405,13 @@ void SML_Send_Seq(uint32_t meter,char *seq) {
if (!rflg) {
*ucp++=0;
*ucp++=2;
+ slen+=2;
}
// append crc
- uint16_t crc = MBUS_calculateCRC(sbuff,6);
+ uint16_t crc = MBUS_calculateCRC(sbuff,slen);
*ucp++=lowByte(crc);
*ucp++=highByte(crc);
- slen+=4;
+ slen+=2;
}
if (script_meter_desc[meter].type=='o') {
for (uint32_t cnt=0;cnt
Date: Sun, 14 Jun 2020 12:36:44 +0200
Subject: [PATCH 32/35] Add rule trigger ``System#Init``
Add rule trigger ``System#Init`` to allow early rule execution without wifi and mqtt initialized yet (#8673)
---
RELEASENOTES.md | 1 +
tasmota/CHANGELOG.md | 1 +
tasmota/settings.h | 6 +++---
tasmota/tasmota.ino | 2 ++
tasmota/xdrv_10_rules.ino | 21 +++++++++++----------
5 files changed, 18 insertions(+), 13 deletions(-)
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index c1637501c..2f9d95ed2 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -78,3 +78,4 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
- Add support for BL0940 energy monitor as used in Blitzwolf BW-SHP10 (#8175)
- Add initial support for Telegram bot (#8619)
- Add support for HP303B Temperature and Pressure sensor by Robert Jaakke (#8638)
+- Add rule trigger ``System#Init`` to allow early rule execution without wifi and mqtt initialized yet
diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md
index 0c6ef82bd..73582dfb7 100644
--- a/tasmota/CHANGELOG.md
+++ b/tasmota/CHANGELOG.md
@@ -4,6 +4,7 @@
- Add initial support for Telegram bot (#8619)
- Add support for HP303B Temperature and Pressure sensor by Robert Jaakke (#8638)
+- Add rule trigger ``System#Init`` to allow early rule execution without wifi and mqtt initialized yet
### 8.3.1.2 20200522
diff --git a/tasmota/settings.h b/tasmota/settings.h
index 1b0cc0951..08175d7db 100644
--- a/tasmota/settings.h
+++ b/tasmota/settings.h
@@ -647,13 +647,14 @@ struct XDRVMAILBOX {
} XdrvMailbox;
#ifdef USE_SHUTTER
-const uint8_t MAX_RULES_FLAG = 10; // Number of bits used in RulesBitfield (tricky I know...)
+const uint8_t MAX_RULES_FLAG = 11; // Number of bits used in RulesBitfield (tricky I know...)
#else
-const uint8_t MAX_RULES_FLAG = 8; // Number of bits used in RulesBitfield (tricky I know...)
+const uint8_t MAX_RULES_FLAG = 9; // Number of bits used in RulesBitfield (tricky I know...)
#endif // USE_SHUTTER
typedef union { // Restricted by MISRA-C Rule 18.4 but so useful...
uint16_t data; // Allow bit manipulation
struct {
+ uint16_t system_init : 1; // Changing layout here needs adjustments in xdrv_10_rules.ino too
uint16_t system_boot : 1;
uint16_t time_init : 1;
uint16_t time_set : 1;
@@ -664,7 +665,6 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
uint16_t http_init : 1;
uint16_t shutter_moved : 1;
uint16_t shutter_moving : 1;
- uint16_t spare10 : 1;
uint16_t spare11 : 1;
uint16_t spare12 : 1;
uint16_t spare13 : 1;
diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino
index 7ee0c8316..06bd8d9e7 100644
--- a/tasmota/tasmota.ino
+++ b/tasmota/tasmota.ino
@@ -322,6 +322,8 @@ void setup(void) {
XdrvCall(FUNC_INIT);
XsnsCall(FUNC_INIT);
+
+ rules_flag.system_init = 1;
}
void BacklogLoop(void) {
diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino
index 10c4c22e8..37300d03c 100644
--- a/tasmota/xdrv_10_rules.ino
+++ b/tasmota/xdrv_10_rules.ino
@@ -887,17 +887,18 @@ void RulesEvery50ms(void)
rules_flag.data ^= mask;
json_event[0] = '\0';
switch (i) {
- case 0: strncpy_P(json_event, PSTR("{\"System\":{\"Boot\":1}}"), sizeof(json_event)); break;
- case 1: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Initialized\":%d}}"), MinutesPastMidnight()); break;
- case 2: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Set\":%d}}"), MinutesPastMidnight()); break;
- case 3: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Connected\":1}}"), sizeof(json_event)); break;
- case 4: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Disconnected\":1}}"), sizeof(json_event)); break;
- case 5: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Connected\":1}}"), sizeof(json_event)); break;
- case 6: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Disconnected\":1}}"), sizeof(json_event)); break;
- case 7: strncpy_P(json_event, PSTR("{\"HTTP\":{\"Initialized\":1}}"), sizeof(json_event)); break;
+ case 0: strncpy_P(json_event, PSTR("{\"System\":{\"Init\":1}}"), sizeof(json_event)); break;
+ case 1: strncpy_P(json_event, PSTR("{\"System\":{\"Boot\":1}}"), sizeof(json_event)); break;
+ case 2: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Initialized\":%d}}"), MinutesPastMidnight()); break;
+ case 3: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Set\":%d}}"), MinutesPastMidnight()); break;
+ case 4: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Connected\":1}}"), sizeof(json_event)); break;
+ case 5: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Disconnected\":1}}"), sizeof(json_event)); break;
+ case 6: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Connected\":1}}"), sizeof(json_event)); break;
+ case 7: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Disconnected\":1}}"), sizeof(json_event)); break;
+ case 8: strncpy_P(json_event, PSTR("{\"HTTP\":{\"Initialized\":1}}"), sizeof(json_event)); break;
#ifdef USE_SHUTTER
- case 8: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moved\":1}}"), sizeof(json_event)); break;
- case 9: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moving\":1}}"), sizeof(json_event)); break;
+ case 9: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moved\":1}}"), sizeof(json_event)); break;
+ case 10: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moving\":1}}"), sizeof(json_event)); break;
#endif // USE_SHUTTER
}
if (json_event[0]) {
From 303b49f2fe09065c01ce691df542b10bcc58fcda Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sun, 14 Jun 2020 15:18:38 +0200
Subject: [PATCH 33/35] use espressif8266@2.5.2
minor fixes in PlatformIO framework
---
platformio.ini | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/platformio.ini b/platformio.ini
index 04835d6c0..abb9d364d 100755
--- a/platformio.ini
+++ b/platformio.ini
@@ -128,6 +128,6 @@ build_flags = ${tasmota_core.build_flags}
[tasmota_core]
; *** Esp8266 Arduino core 2.7.1
-platform = espressif8266@2.5.1
+platform = espressif8266@2.5.2
platform_packages =
build_flags = ${esp82xx_defaults.build_flags}
From 7d1a27560606fc0a0a83d0a0292d44d095d4b455 Mon Sep 17 00:00:00 2001
From: gemu2015
Date: Mon, 15 Jun 2020 07:25:39 +0200
Subject: [PATCH 34/35] fix chartofloat digit overflow
---
tasmota/support.ino | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/tasmota/support.ino b/tasmota/support.ino
index 879a91fb0..0a5d26f10 100644
--- a/tasmota/support.ino
+++ b/tasmota/support.ino
@@ -167,6 +167,8 @@ float CharToFloat(const char *str)
float right = 0;
if (*pt == '.') {
pt++;
+ // limit decimals to float max
+ pt[7]=0;
right = atoi(pt); // Decimal part
while (isdigit(*pt)) {
pt++;
@@ -1908,4 +1910,4 @@ String Decompress(const char * compressed, size_t uncompressed_size) {
return content;
}
-#endif // USE_UNISHOX_COMPRESSION
\ No newline at end of file
+#endif // USE_UNISHOX_COMPRESSION
From 5f7a32af4cfca2fbb9239a7c096190571e88df39 Mon Sep 17 00:00:00 2001
From: gemu2015
Date: Mon, 15 Jun 2020 08:20:43 +0200
Subject: [PATCH 35/35] update ibeacon to export UID
---
tasmota/xsns_52_ibeacon.ino | 29 +++++++++++++++++++++++++----
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/tasmota/xsns_52_ibeacon.ino b/tasmota/xsns_52_ibeacon.ino
index 5f9c0f3ba..6a33d94a0 100755
--- a/tasmota/xsns_52_ibeacon.ino
+++ b/tasmota/xsns_52_ibeacon.ino
@@ -87,6 +87,9 @@ struct IBEACON {
struct IBEACON_UID {
char MAC[12];
char RSSI[4];
+ char UID[32];
+ char MAJOR[4];
+ char MINOR[4];
uint8_t FLAGS;
uint8_t TIME;
} ibeacons[MAX_IBEACONS];
@@ -132,7 +135,7 @@ void hm17_every_second(void) {
ibeacons[cnt].TIME++;
if (ibeacons[cnt].TIME>IB_TIMEOUT_TIME) {
ibeacons[cnt].FLAGS=0;
- ibeacon_mqtt(ibeacons[cnt].MAC,"0000");
+ ibeacon_mqtt(ibeacons[cnt].MAC,"0000",ibeacons[cnt].UID,ibeacons[cnt].MAJOR,ibeacons[cnt].MINOR);
}
}
}
@@ -210,6 +213,9 @@ uint32_t ibeacon_add(struct IBEACON *ib) {
if (!ibeacons[cnt].FLAGS) {
memcpy(ibeacons[cnt].MAC,ib->MAC,12);
memcpy(ibeacons[cnt].RSSI,ib->RSSI,4);
+ memcpy(ibeacons[cnt].UID,ib->UID,32);
+ memcpy(ibeacons[cnt].MAJOR,ib->MAJOR,4);
+ memcpy(ibeacons[cnt].MINOR,ib->MINOR,4);
ibeacons[cnt].FLAGS=1;
ibeacons[cnt].TIME=0;
return 1;
@@ -400,7 +406,7 @@ hm17_v110:
memcpy(ib.RSSI,&hm17_sbuffer[8+8+1+32+1+4+4+2+1+12+1],4);
if (ibeacon_add(&ib)) {
- ibeacon_mqtt(ib.MAC,ib.RSSI);
+ ibeacon_mqtt(ib.MAC,ib.RSSI,ib.UID,ib.MAJOR,ib.MINOR);
}
hm17_sbclr();
hm17_result=1;
@@ -560,15 +566,30 @@ void ib_sendbeep(void) {
hm17_sendcmd(HM17_CON);
}
-void ibeacon_mqtt(const char *mac,const char *rssi) {
+void ibeacon_mqtt(const char *mac,const char *rssi,const char *uid,const char *major,const char *minor) {
char s_mac[14];
+ char s_uid[34];
+ char s_major[6];
+ char s_minor[6];
char s_rssi[6];
memcpy(s_mac,mac,12);
s_mac[12]=0;
+ memcpy(s_uid,uid,32);
+ s_uid[32]=0;
+ memcpy(s_major,major,4);
+ s_major[4]=0;
+ memcpy(s_minor,minor,4);
+ s_minor[4]=0;
memcpy(s_rssi,rssi,4);
s_rssi[4]=0;
int16_t n_rssi=atoi(s_rssi);
- ResponseTime_P(PSTR(",\"" D_CMND_IBEACON "_%s\":{\"RSSI\":%d}}"),s_mac,n_rssi);
+ // if uid == all zeros, take mac
+ if (!strncmp_P(s_uid,PSTR("00000000000000000000000000000000"),32)) {
+ ResponseTime_P(PSTR(",\"" D_CMND_IBEACON "_%s\":{\"UID\":\"%s\",\"MAJOR\":\"%s\",\"MINOR\":\"%s\",\"RSSI\":%d}}"),s_mac,s_uid,s_major,s_minor,n_rssi);
+ } else {
+ ResponseTime_P(PSTR(",\"" D_CMND_IBEACON "_%s\":{\"MAJOR\":\"%s\",\"MINOR\":\"%s\",\"RSSI\":%d}}"),s_uid,s_major,s_minor,n_rssi);
+ }
+
MqttPublishTeleSensor();
}