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/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/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\
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..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)
@@ -753,4 +758,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/settings.ino b/tasmota/settings.ino
index 549799447..f8aa19ab9 100644
--- a/tasmota/settings.ino
+++ b/tasmota/settings.ino
@@ -391,8 +391,7 @@ void UpdateQuickPowerCycle(bool update)
* Config Settings.text char array support
\*********************************************************************************************/
-uint32_t GetSettingsTextLen(void)
-{
+uint32_t GetSettingsTextLen(void) {
char* position = Settings.text_pool;
for (uint32_t size = 0; size < SET_MAX; size++) {
while (*position++ != '\0') { }
@@ -400,8 +399,20 @@ uint32_t GetSettingsTextLen(void)
return position - Settings.text_pool;
}
-bool SettingsUpdateText(uint32_t index, const char* replace_me)
-{
+bool settings_text_mutex = false;
+uint32_t settings_text_busy_count = 0;
+
+bool SettingsUpdateFinished(void) {
+ uint32_t wait_loop = 10;
+ while (settings_text_mutex && wait_loop) { // Wait for any update to finish
+ yield();
+ delayMicroseconds(1);
+ wait_loop--;
+ }
+ return (wait_loop > 0); // true if finished
+}
+
+bool SettingsUpdateText(uint32_t index, const char* replace_me) {
if (index >= SET_MAX) {
return false; // Setting not supported - internal error
}
@@ -438,16 +449,24 @@ bool SettingsUpdateText(uint32_t index, const char* replace_me)
return false; // Replace text too long
}
- if (diff != 0) {
- // Shift Settings.text up or down
- memmove_P(Settings.text_pool + start_pos + replace_len, Settings.text_pool + end_pos, char_len - end_pos);
- }
- // Replace text
- memmove_P(Settings.text_pool + start_pos, replace, replace_len);
- // Fill for future use
- memset(Settings.text_pool + char_len + diff, 0x00, settings_text_size - char_len - diff);
+ if (settings_text_mutex && !SettingsUpdateFinished()) {
+ settings_text_busy_count++;
+ } else {
+ settings_text_mutex = true;
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "CR %d/%d"), GetSettingsTextLen(), settings_text_size);
+ if (diff != 0) {
+ // Shift Settings.text up or down
+ memmove_P(Settings.text_pool + start_pos + replace_len, Settings.text_pool + end_pos, char_len - end_pos);
+ }
+ // Replace text
+ memmove_P(Settings.text_pool + start_pos, replace, replace_len);
+ // Fill for future use
+ memset(Settings.text_pool + char_len + diff, 0x00, settings_text_size - char_len - diff);
+
+ settings_text_mutex = false;
+ }
+
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "CR %d/%d, Busy %d"), GetSettingsTextLen(), settings_text_size, settings_text_busy_count);
return true;
}
@@ -459,6 +478,7 @@ char* SettingsText(uint32_t index)
if (index >= SET_MAX) {
position += settings_text_size -1; // Setting not supported - internal error - return empty string
} else {
+ SettingsUpdateFinished();
for (;index > 0; index--) {
while (*position++ != '\0') { }
}
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_features.ino b/tasmota/support_features.ino
index 787765633..c973e30f5 100644
--- a/tasmota/support_features.ino
+++ b/tasmota/support_features.ino
@@ -575,11 +575,13 @@ void GetFeatures(void)
#ifdef USE_BL0940
feature6 |= 0x00004000; // xnrg_14_bl0940.ino
#endif
+#ifdef USE_TELEGRAM
+ feature6 |= 0x00008000; // xdrv_40_telegram.ino
+#endif
#ifdef USE_HP303B
- feature6 |= 0x00008000; // xsns_73_hp303b.ino
+ feature6 |= 0x00010000; // xsns_73_hp303b.ino
#endif
-// feature6 |= 0x00010000;
// feature6 |= 0x00020000;
// feature6 |= 0x00040000;
// feature6 |= 0x00080000;
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
}
diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h
index c3913e8a1..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};
@@ -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.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
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_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),
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];
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)
diff --git a/tasmota/xdrv_40_telegram.ino b/tasmota/xdrv_40_telegram.ino
new file mode 100644
index 000000000..97c5aa453
--- /dev/null
+++ b/tasmota/xdrv_40_telegram.ino
@@ -0,0 +1,470 @@
+/*
+ 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:
+ * 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
+ * #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_INFO, PSTR("TGM: Started"));
+ }
+
+ init_done = true;
+ }
+ return init_done;
+}
+
+String TelegramConnectToTelegram(String command) {
+// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: 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("TGM: 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;
+}
+
+void TelegramGetUpdates(String offset) {
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: 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("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());
+
+ 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("TGM: Failed to update"));
+ return;
+ }
+ if (0 == i) {
+// 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());
+ }
+
+ 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("TGM: Parsed%d \"%s\""), j, Telegram.message[i][j].c_str());
+ }
+ }
+}
+
+bool TelegramSendMessage(String chat_id, String text) {
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: 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("TGM: Response %s"), response.c_str());
+
+ if (response.startsWith("{\"ok\":true")) {
+// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TGM: Message sent"));
+ sent = true;
+ }
+
+ }
+
+ return sent;
+}
+
+/*
+void TelegramSendGetMe(void) {
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: 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("TGM: 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_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 = "Tm|" // Prefix
+ D_CMND_TMSTATE "|" D_CMND_TMPOLL "|" D_CMND_TMTOKEN "|" D_CMND_TMCHATID "|" D_CMND_TMSEND;
+
+void (* const TelegramCommand[])(void) PROGMEM = {
+ &CmndTmState, &CmndTmPoll, &CmndTmToken, &CmndTmChatId, &CmndTmSend };
+
+void CmndTmState(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 CmndTmPoll(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 CmndTmToken(void) {
+ if (XdrvMailbox.data_len > 0) {
+ SettingsUpdateText(SET_TELEGRAM_TOKEN, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data);
+ }
+ ResponseCmndChar(SettingsText(SET_TELEGRAM_TOKEN));
+}
+
+void CmndTmChatId(void) {
+ if (XdrvMailbox.data_len > 0) {
+ SettingsUpdateText(SET_TELEGRAM_CHATID, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data);
+ }
+ ResponseCmndChar(SettingsText(SET_TELEGRAM_CHATID));
+}
+
+void CmndTmSend(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/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:
diff --git a/tools/decode-status.py b/tools/decode-status.py
index e3402c675..2ec5b472e 100755
--- a/tools/decode-status.py
+++ b/tools/decode-status.py
@@ -205,8 +205,8 @@ 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_HP303B",
- "","","","",
+ "USE_VEML7700","USE_MCP9808","USE_BL0940","USE_TELEGRAM",
+ "USE_HP303B","","","",
"","","","",
"","","","",
"","","","USE_WEBCAM"
@@ -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))