diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index 5723b7b5..a3f438ab 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -639,7 +639,13 @@ PROGMEM const char* const custom_reset_string[] = { #define THINGSPEAK_APIKEY "" // Default API KEY #define THINGSPEAK_USE_ASYNC 1 // Use AsyncClient instead of WiFiClientSecure + +// THINGSPEAK OVER SSL +// Using THINGSPEAK over SSL works well but generates problems with the web interface, +// so you should compile it with WEB_SUPPORT to 0. +// When THINGSPEAK_USE_ASYNC is 1, requires ASYNC_TCP_SSL_ENABLED to 1 and ESP8266 Arduino Core 2.4.0. #define THINGSPEAK_USE_SSL 0 // Use secure connection +#define THINGSPEAK_FINGERPRINT "78 60 18 44 81 35 BF DF 77 84 D4 0A 22 0D 9B 4E 6C DC 57 2C" #define THINGSPEAK_HOST "api.thingspeak.com" #if THINGSPEAK_USE_SSL diff --git a/code/espurna/mqtt.ino b/code/espurna/mqtt.ino index 24a74021..4e4e101a 100644 --- a/code/espurna/mqtt.ino +++ b/code/espurna/mqtt.ino @@ -319,47 +319,6 @@ void _mqttOnMessage(char* topic, char* payload, unsigned int len) { } -#if MQTT_USE_ASYNC - -bool mqttFormatFP(const char * fingerprint, unsigned char * bytearray) { - - // check length (20 2-character digits ':' or ' ' separated => 20*2+19 = 59) - if (strlen(fingerprint) != 59) return false; - - DEBUG_MSG_P(PSTR("[MQTT] Fingerprint %s\n"), fingerprint); - - // walk the fingerprint - for (unsigned int i=0; i<20; i++) { - bytearray[i] = strtol(fingerprint + 3*i, NULL, 16); - } - - return true; - -} - -#else - -bool mqttFormatFP(const char * fingerprint, char * destination) { - - // check length (20 2-character digits ':' or ' ' separated => 20*2+19 = 59) - if (strlen(fingerprint) != 59) return false; - - DEBUG_MSG_P(PSTR("[MQTT] Fingerprint %s\n"), fingerprint); - - // copy it - strncpy(destination, fingerprint, 59); - - // walk the fingerprint replacing ':' for ' ' - for (unsigned char i = 0; i<59; i++) { - if (destination[i] == ':') destination[i] = ' '; - } - - return true; - -} - -#endif - void mqttEnabled(bool status) { _mqtt_enabled = status; setSetting("mqttEnabled", status ? 1 : 0); @@ -423,7 +382,7 @@ void mqttConnect() { if (secure) { DEBUG_MSG_P(PSTR("[MQTT] Using SSL\n")); unsigned char fp[20] = {0}; - if (mqttFormatFP(getSetting("mqttFP", MQTT_SSL_FINGERPRINT).c_str(), fp)) { + if (sslFingerPrintArray(getSetting("mqttFP", MQTT_SSL_FINGERPRINT).c_str(), fp)) { _mqtt.addServerFingerprint(fp); } else { DEBUG_MSG_P(PSTR("[MQTT] Wrong fingerprint\n")); @@ -450,7 +409,7 @@ void mqttConnect() { DEBUG_MSG_P(PSTR("[MQTT] Using SSL\n")); if (_mqtt_client_secure.connect(host, port)) { char fp[60] = {0}; - if (mqttFormatFP(getSetting("mqttFP", MQTT_SSL_FINGERPRINT).c_str(), fp)) { + if (sslFingerPrintChar(getSetting("mqttFP", MQTT_SSL_FINGERPRINT).c_str(), fp)) { if (_mqtt_client_secure.verify(fp, host)) { _mqtt.setClient(_mqtt_client_secure); } else { diff --git a/code/espurna/thinkspeak.ino b/code/espurna/thinkspeak.ino index c2cbea72..b1f9cae0 100644 --- a/code/espurna/thinkspeak.ino +++ b/code/espurna/thinkspeak.ino @@ -10,14 +10,9 @@ Copyright (C) 2018 by Xose PĂ©rez #if THINGSPEAK_USE_ASYNC #include -AsyncClient _tspk_client; +AsyncClient * _tspk_client; #else #include -#if THINGSPEAK_USE_SSL -WiFiClientSecure _tspk_client; -#else -WiFiClient _tspk_client; -#endif #endif const char THINGSPEAK_REQUEST_TEMPLATE[] PROGMEM = @@ -73,22 +68,44 @@ void _tspkConfigure() { void _tspkPost(String data) { - _tspk_client.onError([](void * arg, AsyncClient * client, int error) { - DEBUG_MSG_P(PSTR("[THINGSPEAK] Connection error (%d)\n", error); + if (_tspk_client == NULL) { + _tspk_client = new AsyncClient(); + } + + _tspk_client->onDisconnect([](void *s, AsyncClient *c) { + DEBUG_MSG_P(PSTR("[THINGSPEAK] Disconnected\n")); + _tspk_client->free(); + delete _tspk_client; _tspk_client = NULL; + }, 0); + + _tspk_client->onTimeout([](void *s, AsyncClient *c, uint32_t time) { + _tspk_client->close(true); + }, 0); + + _tspk_client->onData([](void * arg, AsyncClient * c, void * response, size_t len) { + char * b = (char *) response; + b[len] = 0; + char * p = strstr((char *)response, "\r\n\r\n"); + unsigned int code = (p != NULL) ? atoi(&p[4]) : 0; + DEBUG_MSG_P(PSTR("[THINGSPEAK] Response value: %d\n"), code); + _tspk_client->close(true); }, NULL); - _tspk_client.onConnect([data](void * arg, AsyncClient * client) { + _tspk_client->onConnect([data](void * arg, AsyncClient * client) { - DEBUG_MSG_P(PSTR("[THINGSPEAK] POST %s\n"), data.c_str()); + DEBUG_MSG_P(PSTR("[THINGSPEAK] Connected to %s:%d\n"), THINGSPEAK_HOST, THINGSPEAK_PORT); - client->onData([](void * arg, AsyncClient * c, void * response, size_t len) { - char * b = (char *) response; - b[len] = 0; - char * p = strstr((char *)response, "\r\n\r\n"); - unsigned int code = (p != NULL) ? atoi(&p[4]) : 0; - DEBUG_MSG_P(PSTR("[THINGSPEAK] Response value: %d\n"), code); - }, NULL); + #if THINGSPEAK_USE_SSL + uint8_t fp[20] = {0}; + sslFingerPrintArray(THINGSPEAK_FINGERPRINT, fp); + SSL * ssl = _tspk_client->getSSL(); + if (ssl_match_fingerprint(ssl, fp) != SSL_OK) { + DEBUG_MSG_P(PSTR("[THINGSPEAK] Warning: certificate doesn't match\n")); + } + #endif + + DEBUG_MSG_P(PSTR("[THINGSPEAK] POST %s\n"), data.c_str()); char buffer[strlen_P(THINGSPEAK_REQUEST_TEMPLATE) + strlen(THINGSPEAK_URL) + strlen(THINGSPEAK_HOST) + data.length()]; snprintf_P(buffer, sizeof(buffer), @@ -104,14 +121,15 @@ void _tspkPost(String data) { }, NULL); #if ASYNC_TCP_SSL_ENABLED - if (!_tspk_client.connect(THINGSPEAK_HOST, THINGSPEAK_PORT, THINGSPEAK_USE_SSL)) { - DEBUG_MSG_P(PSTR("[THINGSPEAK] Connection failed\n")); - } - #else // ASYNC_TCP_SSL_ENABLED - if (!_tspk_client.connect(THINGSPEAK_HOST, THINGSPEAK_PORT)) { - DEBUG_MSG_P(PSTR("[THINGSPEAK] Connection failed\n")); - } - #endif // ASYNC_TCP_SSL_ENABLED + bool connected = _tspk_client->connect(THINGSPEAK_HOST, THINGSPEAK_PORT, THINGSPEAK_USE_SSL); + #else + bool connected = _tspk_client->connect(THINGSPEAK_HOST, THINGSPEAK_PORT); + #endif + + if (!connected) { + DEBUG_MSG_P(PSTR("[THINGSPEAK] Connection failed\n")); + _tspk_client->close(true); + } } @@ -119,10 +137,21 @@ void _tspkPost(String data) { void _tspkPost(String data) { + #if THINGSPEAK_USE_SSL + WiFiClientSecure _tspk_client; + #else + WiFiClient _tspk_client; + #endif + if (_tspk_client.connect(THINGSPEAK_HOST, THINGSPEAK_PORT)) { - DEBUG_MSG_P(PSTR("[THINGSPEAK] POST %s\n"), data.c_str()); + DEBUG_MSG_P(PSTR("[THINGSPEAK] Connected to %s:%d\n"), THINGSPEAK_HOST, THINGSPEAK_PORT); + if (!_tspk_client.verify(THINGSPEAK_FINGERPRINT, THINGSPEAK_HOST)) { + DEBUG_MSG_P(PSTR("[THINGSPEAK] Warning: certificate doesn't match\n")); + } + + DEBUG_MSG_P(PSTR("[THINGSPEAK] POST %s\n"), data.c_str()); char buffer[strlen_P(THINGSPEAK_REQUEST_TEMPLATE) + strlen(THINGSPEAK_URL) + strlen(THINGSPEAK_HOST) + data.length()]; snprintf_P(buffer, sizeof(buffer), THINGSPEAK_REQUEST_TEMPLATE, diff --git a/code/espurna/utils.ino b/code/espurna/utils.ino index 04fa9fc2..1aca5b6d 100644 --- a/code/espurna/utils.ino +++ b/code/espurna/utils.ino @@ -201,6 +201,51 @@ void heartbeat() { } +// ----------------------------------------------------------------------------- +// SSL +// ----------------------------------------------------------------------------- + +#if ASYNC_TCP_SSL_ENABLED + +bool sslCheckFingerPrint(const char * fingerprint) { + return (strlen(fingerprint) == 59); +} + +bool sslFingerPrintArray(const char * fingerprint, unsigned char * bytearray) { + + // check length (20 2-character digits ':' or ' ' separated => 20*2+19 = 59) + if (!sslCheckFingerPrint(fingerprint)) return false; + + // walk the fingerprint + for (unsigned int i=0; i<20; i++) { + bytearray[i] = strtol(fingerprint + 3*i, NULL, 16); + } + + return true; + +} + +bool sslFingerPrintChar(const char * fingerprint, char * destination) { + + // check length (20 2-character digits ':' or ' ' separated => 20*2+19 = 59) + if (!sslCheckFingerPrint(fingerprint)) return false; + + // copy it + strncpy(destination, fingerprint, 59); + + // walk the fingerprint replacing ':' for ' ' + for (unsigned char i = 0; i<59; i++) { + if (destination[i] == ':') destination[i] = ' '; + } + + return true; + +} + +#endif + +// ----------------------------------------------------------------------------- +// Reset // ----------------------------------------------------------------------------- unsigned char resetReason() {