diff --git a/.ci/arduino.groovy b/.ci/arduino.groovy index 051271c03..05ff16797 100644 --- a/.ci/arduino.groovy +++ b/.ci/arduino.groovy @@ -51,6 +51,7 @@ def buildMySensorsMicro(config, sketches, String key) { for (sketch = 0; sketch < sketches.size(); sketch++) { if (sketches[sketch].path != config.library_root+'examples/GatewayESP8266/GatewayESP8266.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266SecureMQTTClient/GatewayESP8266SecureMQTTClient.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266OTA/GatewayESP8266OTA.ino' && sketches[sketch].path != config.library_root+'examples/GatewayGSMMQTTClient/GatewayGSMMQTTClient.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP32/GatewayESP32.ino' && @@ -87,6 +88,7 @@ def buildMySensorsGw(config, sketches, String key) { if (sketches[sketch].path != config.library_root+'examples/BatteryPoweredSensor/BatteryPoweredSensor.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266/GatewayESP8266.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266SecureMQTTClient/GatewayESP8266SecureMQTTClient.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266OTA/GatewayESP8266OTA.ino' && sketches[sketch].path != config.library_root+'examples/GatewayGSMMQTTClient/GatewayGSMMQTTClient.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP32/GatewayESP32.ino' && @@ -123,6 +125,7 @@ def buildArduinoUno(config, sketches, String key) { for (sketch = 0; sketch < sketches.size(); sketch++) { if (sketches[sketch].path != config.library_root+'examples/GatewayESP8266/GatewayESP8266.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266SecureMQTTClient/GatewayESP8266SecureMQTTClient.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266OTA/GatewayESP8266OTA.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP32/GatewayESP32.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP32OTA/GatewayESP32OTA.ino' && @@ -157,6 +160,7 @@ def buildArduinoMega(config, sketches, String key) { for (sketch = 0; sketch < sketches.size(); sketch++) { if (sketches[sketch].path != config.library_root+'examples/GatewayESP8266/GatewayESP8266.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266SecureMQTTClient/GatewayESP8266SecureMQTTClient.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266OTA/GatewayESP8266OTA.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP32/GatewayESP32.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP32OTA/GatewayESP32OTA.ino' && @@ -191,6 +195,7 @@ def buildSTM32F1(config, sketches, String key) { for (sketch = 0; sketch < sketches.size(); sketch++) { if (sketches[sketch].path != config.library_root+'examples/GatewayESP8266/GatewayESP8266.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266SecureMQTTClient/GatewayESP8266SecureMQTTClient.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266OTA/GatewayESP8266OTA.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP32/GatewayESP32.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP32OTA/GatewayESP32OTA.ino' && @@ -280,6 +285,7 @@ def buildESP32(config, sketches, String key) { sketches[sketch].path != config.library_root+'examples/GatewayGSMMQTTClient/GatewayGSMMQTTClient.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266/GatewayESP8266.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266SecureMQTTClient/GatewayESP8266SecureMQTTClient.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266OTA/GatewayESP8266OTA.ino' && sketches[sketch].path != config.library_root+'examples/SensebenderGatewaySerial/SensebenderGatewaySerial.ino' && sketches[sketch].path != config.library_root+'examples/MotionSensorRS485/MotionSensorRS485.ino' && @@ -316,6 +322,7 @@ def buildnRF5(config, sketches, String key) { sketches[sketch].path != config.library_root+'examples/DustSensorDSM/DustSensorDSM.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266/GatewayESP8266.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266MQTTClient/GatewayESP8266MQTTClient.ino' && + sketches[sketch].path != config.library_root+'examples/GatewayESP8266SecureMQTTClient/GatewayESP8266SecureMQTTClient.ino' && sketches[sketch].path != config.library_root+'examples/GatewayGSMMQTTClient/GatewayGSMMQTTClient.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP8266OTA/GatewayESP8266OTA.ino' && sketches[sketch].path != config.library_root+'examples/GatewayESP32/GatewayESP32.ino' && @@ -396,4 +403,4 @@ def buildnRF51822(config, sketches, String key) { } } -return this \ No newline at end of file +return this diff --git a/MyConfig.h b/MyConfig.h index ca1f0e0d4..727bdf8fd 100755 --- a/MyConfig.h +++ b/MyConfig.h @@ -1472,6 +1472,8 @@ * @brief Define this for Ethernet GW based on the ENC28J60 module. * @def MY_GATEWAY_ESP8266 * @brief Define this for Ethernet GW based on the ESP8266. + * @def MY_GATEWAY_ESP8266_SECURE + * @brief Define this for Ethernet GW based on the ESP8266 with TLS. * @def MY_GATEWAY_ESP32 * @brief Define this for Ethernet GW based on the ESP32. * @def MY_GATEWAY_LINUX @@ -1487,6 +1489,7 @@ //#define MY_GATEWAY_W5100 //#define MY_GATEWAY_ENC28J60 //#define MY_GATEWAY_ESP8266 +//#define MY_GATEWAY_ESP8266_SECURE //#define MY_GATEWAY_ESP32 //#define MY_GATEWAY_LINUX //#define MY_GATEWAY_TINYGSM @@ -1594,29 +1597,79 @@ //#define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "mygateway1-in" /** - * @def MY_MQTT_CA_CERT - * @brief Set a specific CA certificate needed to validate MQTT server against. Use the certificate as a trust anchor, accepting remote certificates signed by it. + * @def MY_MQTT_CA_CERT1 + * @brief Up to three root Certificates Authorities could be defined to validate the mqtt server' certificate. The most secure. + * + * This define is mandatory when you need connect MQTT over SSL/TLS. Certificate Authorities. + * The best method to validate server certificates. + * Advised to retrieve root Certificate Authorities as they expire less often than server certificates. + * With let's encrypt you may need up to three Certificate Authorities * - * This define is mandatory when you need connect MQTT over SSL/TLS. * Example: @code * - * const char mqtt_ca_cert[] PROGMEM = R"EOF( + * const char cert_isrgrootx1_Authority[] PROGMEM = R"EOF( * ----- BEGIN THE CERTIFICATE ----- * XXX ... XXX * ----- FINISH CERTIFICATE ----- * )EOF"; * - * #define MY_MQTT_CA_CERT mqtt_ca_cert + * const char cert_isrgrootx2_Authority[] PROGMEM = R"EOF( + * ----- BEGIN THE CERTIFICATE ----- + * XXX ... XXX + * ----- FINISH CERTIFICATE ----- + * )EOF"; + * + * const char cert_letsEncryptR3_Authority[] PROGMEM = R"EOF( + * ----- BEGIN THE CERTIFICATE ----- + * XXX ... XXX + * ----- FINISH CERTIFICATE ----- + * )EOF"; + * + * #define MY_MQTT_CA_CERT1 cert_isrgrootx1_Authority + * #define MY_MQTT_CA_CERT2 cert_isrgrootx2_Authority + * #define MY_MQTT_CA_CERT3 cert_letsEncryptR3_Authority + * + * @endcode + */ +//#define MY_MQTT_CA_CERT1 + +/** + * @def MY_MQTT_CA_CERT2 + * @brief Up to three root Certificates Authorities could be defined to validate the mqtt serv. +*/ +//#define MY_MQTT_CA_CERT2 + +/** + * @def MY_MQTT_CA_CERT3 + * @brief Up to three root Certificates Authorities could be defined to validate the mqtt serv. +*/ +//#define MY_MQTT_CA_CERT3 + + +/** + * @def MY_MQTT_FINGERPRINT + * @brief Server certificate validation with its fingerprint + * + * The finger print to validate the mqtt server certificate. This is less secure and less convenient + * than using certificate authorities. + * Command (3 lines...) to obtain the certificate finger print: + * @code + * $>openssl s_client -connect : < /dev/null 2>/dev/null | \ + * openssl x509 -fingerprint -noout -in /dev/stdin \ + * awk -F= '{print $2}' + * @endcode * + * Example: @code + * const char mqtt_fingerprint [] PROGMEM = "CA:CE:2B:MD:D3:32:A3:F1:8C:73:9E:1B:B7:D5:75:4A:10:61:E4:05"; * @endcode */ -//#define MY_MQTT_CA_CERT +//#define MY_MQTT_FINGERPRINT /** * @def MY_MQTT_CLIENT_CERT * @brief Set a client certificate to send to a MQTT server that requests one over TLS connection. * - * This define is mandatory when you need connect MQTT over SSL/TLS. + * This define is mandatory when you need connect MQTT over SSL/TLS and client certificate is requested. * Example: @code * * const char mqtt_client_cert[] PROGMEM = R"EOF( @@ -1633,9 +1686,9 @@ /** * @def MY_MQTT_CLIENT_KEY - * @brief Set a client private key to send to a MQTT server that requests one over TLS connection. + * @brief Set the client private key generated with the MY_MQTT_CLIENT_CERT. * - * This define is mandatory when you need connect MQTT over SSL/TLS. + * This define is mandatory when you need connect MQTT over SSL/TLS and client certificate is requested. * Example: @code * * const char mqtt_client_key[] PROGMEM = R"EOF( @@ -2419,7 +2472,10 @@ #define MY_MQTT_CLIENT_ID #define MY_MQTT_PUBLISH_TOPIC_PREFIX #define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX -#define MY_MQTT_CA_CERT +#define MY_MQTT_CA_CERT1 +#define MY_MQTT_CA_CERT2 +#define MY_MQTT_CA_CERT3 +#define MY_MQTT_FINGERPRINT #define MY_MQTT_CLIENT_CERT #define MY_MQTT_CLIENT_KEY #define MY_SIGNAL_REPORT_ENABLED diff --git a/core/MyGatewayTransportMQTTClient.cpp b/core/MyGatewayTransportMQTTClient.cpp old mode 100755 new mode 100644 index bd5ab5f58..3b3096dc4 --- a/core/MyGatewayTransportMQTTClient.cpp +++ b/core/MyGatewayTransportMQTTClient.cpp @@ -17,6 +17,29 @@ * version 2 as published by the Free Software Foundation. */ +/* + * Modified by Eric Grammatico + * + * Added support to secured connexion to mqtt server thanks to WiFiClientSecure class. + * Please see comments in code. You can look for WiFiClientSecure, MY_GATEWAY_ESP8266_SECURE, + * MY_MQTT_CA_CERT, MY_MQTT_FINGERPRINT and MY_MQTT_CLIENT_CERT in the code below to see what has + * changed. No new method, no new class to be used by my_sensors. + * + * The following constants have to be defined from the gateway code: + * MY_GATEWAY_ESP8266_SECURE in place of MY_GATEWAY_ESP8266 to go to secure connexions. + * MY_MQTT_CA_CERTx Up to three root Certificates Authorities could be defined + * to validate the mqtt server' certificate. The most secure. + * MY_MQTT_FINGERPRINT Alternatively, the mqtt server' certificate finger print + * could be used. Less secure and less convenient as you'll + * have to update the fingerprint each time the mqtt server' + * certificate is updated + * If neither MY_MQTT_CA_CERT1 nor MY_MQTT_FINGERPRINT are + * defined, insecure connexion will be established. The mqtt + * server' certificate will not be validated. + * MY_MQTT_CLIENT_CERT The mqtt server may require client certificate for + * MY_MQTT_CLIENT_KEY authentication. + * + */ // Topic structure: MY_MQTT_PUBLISH_TOPIC_PREFIX/NODE-ID/SENSOR-ID/CMD-TYPE/ACK-FLAG/SUB-TYPE @@ -47,6 +70,12 @@ #undef MY_ESP8266_HOSTNAME // cleanup #endif +#ifdef MY_MQTT_CA_CERT +#warning MY_MQTT_CA_CERT is deprecated, please use MY_MQTT_CA_CERT1 instead! +#define MY_MQTT_CA_CERT1 MY_MQTT_CA_CERT +//#undef MY_MQTT_CA_CERT // cleanup +#endif + #ifndef MY_MQTT_USER #define MY_MQTT_USER NULL #endif @@ -55,7 +84,7 @@ #define MY_MQTT_PASSWORD NULL #endif -#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) +#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP8266_SECURE) || defined(MY_GATEWAY_ESP32) #if !defined(MY_WIFI_SSID) #error ESP8266/ESP32 MQTT gateway: MY_WIFI_SSID not defined! #endif @@ -69,7 +98,7 @@ #define _MQTT_clientIp IPAddress(MY_IP_ADDRESS) #if defined(MY_IP_GATEWAY_ADDRESS) #define _gatewayIp IPAddress(MY_IP_GATEWAY_ADDRESS) -#elif defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) +#elif defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP8266_SECURE) || defined(MY_GATEWAY_ESP32) // Assume the gateway will be the machine on the same network as the local IP // but with last octet being '1' #define _gatewayIp IPAddress(_MQTT_clientIp[0], _MQTT_clientIp[1], _MQTT_clientIp[2], 1) @@ -77,20 +106,42 @@ #if defined(MY_IP_SUBNET_ADDRESS) #define _subnetIp IPAddress(MY_IP_SUBNET_ADDRESS) -#elif defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) +#elif defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP8266_SECURE) || defined(MY_GATEWAY_ESP32) #define _subnetIp IPAddress(255, 255, 255, 0) #endif /* End of MY_IP_SUBNET_ADDRESS */ #endif /* End of MY_IP_ADDRESS */ #if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) -#if defined(MY_MQTT_CA_CERT) && defined(MY_MQTT_CLIENT_CERT) && defined(MY_MQTT_CLIENT_KEY) -#define EthernetClient WiFiClientSecure -BearSSL::X509List ca_cert(MY_MQTT_CA_CERT); -BearSSL::X509List client_cert(MY_MQTT_CLIENT_CERT); -BearSSL::PrivateKey client_key(MY_MQTT_CLIENT_KEY); -#else #define EthernetClient WiFiClient -#endif /* End of MY_MQTT_CA_CERT && MY_MQTT_CLIENT_CERT && MY_MQTT_CLIENT_KEY */ +#elif defined(MY_GATEWAY_ESP8266_SECURE) +#define EthernetClient WiFiClientSecure +#if defined(MY_MQTT_CA_CERT1) +BearSSL::X509List certAuth; //List to store Certificat Authorities +#endif +#if defined(MY_MQTT_CLIENT_CERT) && defined(MY_MQTT_CLIENT_KEY) +BearSSL::X509List clientCert; //Client public key +BearSSL::PrivateKey clientPrivKey; //Client private key +#endif +// Set time via NTP, as required for x.509 validation +// BearSSL checks NotBefore and NotAfter dates in certificates +// Thus an approximated date/time is needed. +void setClock() +{ + configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov"); + + Serial.print("Waiting for NTP time sync: "); + time_t now = time(nullptr); + while (now < 8 * 3600 * 2) { + delay(500); + Serial.print("."); + now = time(nullptr); + } + Serial.println(""); + struct tm timeinfo; + gmtime_r(&now, &timeinfo); + Serial.print("Current time: "); + Serial.print(asctime(&timeinfo)); +} #elif defined(MY_GATEWAY_LINUX) // Nothing to do here #else @@ -146,13 +197,18 @@ bool reconnectMQTT(void) { GATEWAY_DEBUG(PSTR("GWT:RMQ:CONNECTING...\n")); +#if defined(MY_GATEWAY_ESP8266_SECURE) + // Date/time are retrieved to be able to validate certificates. + setClock(); +#endif + // Attempt to connect if (_MQTT_client.connect(MY_MQTT_CLIENT_ID, MY_MQTT_USER, MY_MQTT_PASSWORD)) { GATEWAY_DEBUG(PSTR("GWT:RMQ:OK\n")); // Send presentation of locally attached sensors (and node if applicable) presentNode(); // Once connected, publish subscribe - char inTopic[strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + strlen("/+/+/+/+/+")]; + char inTopic[strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + strlen("/+/+/+/+/+") + 1]; (void)strncpy(inTopic, MY_MQTT_SUBSCRIBE_TOPIC_PREFIX, strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + 1); (void)strcat(inTopic, "/+/+/+/+/+"); _MQTT_client.subscribe(inTopic); @@ -161,18 +217,24 @@ bool reconnectMQTT(void) } delay(1000); GATEWAY_DEBUG(PSTR("!GWT:RMQ:FAIL\n")); +#if defined(MY_GATEWAY_ESP8266_SECURE) + char sslErr[256]; + int errID = _MQTT_ethClient.getLastSSLError(sslErr, sizeof(sslErr)); + GATEWAY_DEBUG(PSTR("!GWT:RMQ:(%d) %s\n"), errID, sslErr); +#endif return false; } bool gatewayTransportConnect(void) { -#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) +#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP8266_SECURE) || defined(MY_GATEWAY_ESP32) if (WiFi.status() != WL_CONNECTED) { GATEWAY_DEBUG(PSTR("GWT:TPC:CONNECTING...\n")); delay(1000); return false; } GATEWAY_DEBUG(PSTR("GWT:TPC:IP=%s\n"), WiFi.localIP().toString().c_str()); + #elif defined(MY_GATEWAY_LINUX) #if defined(MY_IP_ADDRESS) _MQTT_ethClient.bind(_MQTT_clientIp); @@ -250,10 +312,10 @@ bool gatewayTransportInit(void) _MQTT_client.setCallback(incomingMQTT); -#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) +#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP8266_SECURE) || defined(MY_GATEWAY_ESP32) // Turn off access point WiFi.mode(WIFI_STA); -#if defined(MY_GATEWAY_ESP8266) +#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP8266_SECURE) WiFi.hostname(MY_HOSTNAME); #elif defined(MY_GATEWAY_ESP32) WiFi.setHostname(MY_HOSTNAME); @@ -264,10 +326,36 @@ bool gatewayTransportInit(void) (void)WiFi.begin(MY_WIFI_SSID, MY_WIFI_PASSWORD, 0, MY_WIFI_BSSID); #endif -#if defined(MY_MQTT_CA_CERT) && defined(MY_MQTT_CLIENT_CERT) && defined(MY_MQTT_CLIENT_KEY) - _MQTT_ethClient.setTrustAnchors(&ca_cert); - _MQTT_ethClient.setClientRSACert(&client_cert, &client_key); -#endif /* End of MY_MQTT_CA_CERT && MY_MQTT_CLIENT_CERT && MY_MQTT_CLIENT_KEY */ +#if defined(MY_GATEWAY_ESP8266_SECURE) + // Certificate Authorities are stored in the X509 list + // At least one is needed, but you may need two, or three + // eg to validate one certificate from LetsEncrypt two is needed +#if defined(MY_MQTT_CA_CERT1) + certAuth.append(MY_MQTT_CA_CERT1); +#if defined(MY_MQTT_CA_CERT2) + certAuth.append(MY_MQTT_CA_CERT2); +#endif +#if defined(MY_MQTT_CA_CERT3) + certAuth.append(MY_MQTT_CA_CERT3); +#endif + _MQTT_ethClient.setTrustAnchors(&certAuth); +#elif defined(MY_MQTT_FINGERPRINT) //MY_MQTT_CA_CERT1 + // Alternatively, the certificate could be validated with its + // fingerprint, which is less secure + _MQTT_ethClient.setFingerprint(MY_MQTT_FINGERPRINT); +#else //MY_MQTT_CA_CERT1 + // At last, an insecure connexion is accepted. Meaning the + // server's certificate is not validated. + _MQTT_ethClient.setInsecure(); + GATEWAY_DEBUG(PSTR("GWT:TPC:CONNECTING WITH INSECURE SETTING...\n")); +#endif //MY_MQTT_CA_CERT1 +#if defined(MY_MQTT_CLIENT_CERT) && defined(MY_MQTT_CLIENT_KEY) + // The server may required client certificate + clientCert.append(MY_MQTT_CLIENT_CERT); + clientPrivKey.parse(MY_MQTT_CLIENT_KEY); + _MQTT_ethClient.setClientRSACert(&clientCert, &clientPrivKey); +#endif +#endif //MY_GATEWAY_ESP8266_SECURE gatewayTransportConnect(); @@ -280,7 +368,7 @@ bool gatewayTransportAvailable(void) if (_MQTT_connecting) { return false; } -#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP32) +#if defined(MY_GATEWAY_ESP8266) || defined(MY_GATEWAY_ESP8266_SECURE) || defined(MY_GATEWAY_ESP32) if (WiFi.status() != WL_CONNECTED) { #if defined(MY_GATEWAY_ESP32) (void)gatewayTransportInit(); diff --git a/core/MySigning.h b/core/MySigning.h index 1b29f2cef..07ea57753 100644 --- a/core/MySigning.h +++ b/core/MySigning.h @@ -44,15 +44,15 @@ * * If you do want the additional security layer signing provides, you pick the backend of your * choice in your sketch. Currently, two compatible backends are supported; @ref MY_SIGNING_ATSHA204 - * (hardware backed) and @ref MY_SIGNING_SOFT (software backed). There also exist a simplified - * variant (@ref MY_SIGNING_SIMPLE_PASSWD) of the software backend which only require one setting + * (hardware backed) and @ref MY_SIGNING_SOFT (software backed). There also exists a simplified + * variant (@ref MY_SIGNING_SIMPLE_PASSWD) of the software backend which only requires one setting * to activate. * * If you use hardware backed signing, then connect the device as follows: * @image html MySigning/wiring.png * @note The pull-up resistor is optional but recommended. * @note If you change the default pin (A3) make sure you use a pin that supports input/output - * (ex. A6 & A7 on a Pro Mini are input only pins). + * (e.g. A6 & A7 on a Pro Mini are input only pins). * * To use signing, you need to perform three major steps which are described below. * @@ -65,12 +65,12 @@ * ... * @endcode * Make sure to set the define before the inclusion of MySensors.h. - * It is ok to mix @ref MY_SIGNING_SOFT and @ref MY_SIGNING_ATSHA204 in a network. + * It is OK to mix @ref MY_SIGNING_SOFT and @ref MY_SIGNING_ATSHA204 in a network. * They are fully compatible. It is however not recommended to use @ref MY_SIGNING_SOFT on nodes * that are publicly accessible (more on that later). * * If you use @ref MY_SIGNING_SOFT or @ref MY_SIGNING_ATSHA204 you also need to decide if the node - * (or gateway) in question require messages to be signed in addition to the ability to generate + * (or gateway) in question requires messages to be signed in addition to the ability of generating * signatures for other nodes. * This has to be set by at least one of the nodes in a "pair" or nobody will actually start * calculating a signature for a message. @@ -85,17 +85,17 @@ * If @ref MY_SIGNING_WEAK_SECURITY is not set, any node that has presented itself with * signing/whitelisting requirements will be permanently marked as such by the receiver * (typically the gateway). The only way then to reset/revert this requirement is to clear the - * EEPROM at the receiver (or disable @ref MY_SIGNING_REQUEST_SIGNATURES, but the preference will be + * EEPROM of the receiver (or disable @ref MY_SIGNING_REQUEST_SIGNATURES, but the preference will be * remembered if the request flag is re-enabled before EEPROM is cleared).
* If you want to have two nodes communicate securely directly with each other, the nodes that - * require signatures must send a presentation message to all nodes it expect signed messages from + * require signatures must send a presentation message to all nodes from which it expects signed messages * (only the gateway is informed automatically). See @ref signerPresentation().
* A node can have three "states" with respect to signing: * 1. Node does not support signing in any way (neither @ref MY_SIGNING_ATSHA204, * @ref MY_SIGNING_SOFT nor @ref MY_SIGNING_SIMPLE_PASSWD is set) - * 2. Node does support signing but don't require messages sent to it to be signed (neither + * 2. Node does support signing but donesn't require messages sent to it to be signed (neither * @ref MY_SIGNING_REQUEST_SIGNATURES nor @ref MY_SIGNING_SIMPLE_PASSWD is set) - * 3. Node does support signing and require messages sent to it to be signed (@ref + * 3. Node does support signing and requires messages sent to it to be signed (@ref * MY_SIGNING_SOFT or @ref MY_SIGNING_ATSHA204 together with @ref MY_SIGNING_REQUEST_SIGNATURES or * @ref MY_SIGNING_SIMPLE_PASSWD are set) * @@ -114,10 +114,10 @@ * #include * ... * @endcode - * For the software backed signing backend, an unconnected analog pin is required on boards that - * does not provide a hardware based random generator unit to set a random seed for the + * For the software backed signing backend on a board that does not provide a hardware based random + * generator unit, an unconnected analog pin is required in order to set a random seed for the * pseudo-random generator. - * It is important that the pin is floating, or the output of the pseudo-random generator will be + * It is important that this pin is floating, or the output of the pseudo-random generator will be * predictable, and thus compromise the signatures. The setting is defined using * @ref MY_SIGNING_SOFT_RANDOMSEED_PIN. The same configuration possibilities exist as with the other * configuration options. @@ -130,7 +130,7 @@ * ... * @endcode * - * An example of a node that require signatures is available in @ref SecureActuator.ino. + * An example of a node that requires signatures is available in @ref SecureActuator.ino. * * Thirdly, if you use a signing backend and you don't use @ref MY_SIGNING_SIMPLE_PASSWD, you * need to personalize the node. @@ -138,7 +138,7 @@ * @anchor personalization If you use the ATSHA204A (@ref MY_SIGNING_ATSHA204), before any signing * operations can be done, the device needs to be personalized. * This can be a daunting process as it involves irreversibly writing configurations to the device, - * which cannot be undone. I have however tried to simplify the process as much as possibly by + * which cannot be undone. I have however tried to simplify the process as much as possible by * creating a helper-sketch specifically for this purpose in @ref SecurityPersonalizer.ino * Note that you also need to do personalization for @ref MY_SIGNING_SOFT, but then the values are * stored in EEPROM. @@ -146,7 +146,7 @@ * To personalize a ATSHA204A do the following procedure: * 1. Enable @ref GENERATE_KEYS_ATSHA204A
* This will lock the ATSHA204A and generate random keys for HMAC (signing) and %AES (encryption). - * Copy the keys generated and replace the corresponding definitions under + * Copy the generated keys and replace the corresponding definitions under * "User defined key data", specifically @ref MY_HMAC_KEY and @ref MY_AES_KEY. * 2. Disable @ref GENERATE_KEYS_ATSHA204A and enable @ref PERSONALIZE_ATSHA204A
* This will store the HMAC key to the ATSHA204A and the %AES key to EEPROM. It will also write @@ -157,7 +157,7 @@ * To personalize for software signing do the following procedure: * 1. Enable @ref GENERATE_KEYS_SOFT
* This will generate random keys for HMAC (signing) and %AES (encryption). - * Copy the keys generated and replace the corresponding definitions under + * Copy the generated keys and replace the corresponding definitions under * "User defined key data", specifically @ref MY_HMAC_KEY and @ref MY_AES_KEY. * 2. Disable @ref GENERATE_KEYS_SOFT and enable @ref PERSONALIZE_SOFT
* This will store the HMAC key and the %AES key to EEPROM. It will also write @@ -172,7 +172,7 @@ * under "Hardware security peripherals" to determine if this is necessary. * * When you have personalized your first device after step 2 above, you can run the same sketch on - * all devices in your network that needs to be personalized in a compatible manner. Pick + * all devices in your network that need to be personalized in a compatible manner. Pick * @ref PERSONALIZE_ATSHA204A or @ref PERSONALIZE_SOFT as needed by the hardware. When the * personalization has finished, you just program the sketch you plan to use (with the appropriate * signing flags set). @@ -195,12 +195,12 @@ * related message, it will fail, and enabling signing will put maximum strain on your RF link as * maximum sized packets are transmitted in the network). See @ref MySigningTroubleshootinggrp. * * All nodes and gateways in a network maintain a table where the signing preferences of all nodes - * are stored. This is also stored in EEPROM so if a node or gateway reboots, the other nodes does + * are stored. This is also stored in EEPROM so if a node or gateway reboots, the other nodes do * not have to retransmit a signing presentation to the node for the node to start expecting signed * messages from other nodes.
* * By default, the signing preferences are not "downgradeable". That is, any node that at any * point in time has indicated a signing requirement will not be able to revert this requirement at - * the receiving end (except by manual erase of the EEPROM).
+ * the receiving end (except by manually erasing the EEPROM).
* If you for some reason need to be able to downgrade the security requirements, you can set * @ref MY_SIGNING_WEAK_SECURITY at the receiver to allow it to downgrade the security expectations * of the node in question.
@@ -225,13 +225,13 @@ * * One day your keyfob gets stolen or you lost it or it simply broke down. * - * You now end up with a problem; you need some way of telling your door node that the keyfob in - * question cannot be trusted any more. You could now repersonalize all your node to switch to a + * You now face a problem; you need some way of telling your door node that the keyfob in + * question cannot be trusted any more. You could now repersonalize all your nodes to switch to a * different PSK but this obviously is a hassle. How do you make sure that the "rogue" keyfob can be * removed from the "trusted chain"? * * The answer to this is whitelisting. You let your door node keep a whitelist of all nodes it - * trusts. If you stop trusting a particular node, you remove it from the nodes whitelist + * trusts. If you stop trusting a particular node, you remove it from the node's whitelist * (by uploading a new sketch), and it will no longer be able to communicate signed messages to the * door node. * @@ -239,7 +239,7 @@ * case they are lost) you also need to take note of the serial number of the ATSHA device or the * software value stored in EEPROM. This is unique for each device. The serial number is printed * in a copy+paste friendly format by the personalizer for this purpose.
- * The whitelist is stored on the node that require signatures. When a received message is + * The whitelist is stored on the node that requires signatures. When a received message is * verified, the serial of the sender is looked up in a list stored on the receiving node, and the * corresponding serial stored in the list for that sender is then included in the signature * verification process. The list is stored as the value of the flag that enables whitelisting, @@ -249,18 +249,18 @@ * the receiver. In the case of @ref MY_SIGNING_ATSHA204 this is the unique serial number programmed * into the circuit. This unique number is never transmitted over the air in clear text, so Eve will * not be able to figure out a "trusted" serial by snooping on the traffic.
- * Instead the value is hashed together with the senders NodeId into the HMAC signature to produce + * Instead the value is hashed together with the sender's NodeId into the HMAC signature to produce * the final signature. The receiver will then take the originating NodeId of the signed message and - * do the corresponding calculation with the serial it has stored in it's whitelist if it finds a - * matching entry in it's whitelist. + * do the corresponding calculation with the serial it has stored in its whitelist if it finds a + * matching entry in its whitelist. * * Whitelisting is an optional alternative because it adds some code and configuration options which * might not be desirable for every user. So if you want the ability to use whitelists, you need to * enable @ref MY_SIGNING_NODE_WHITELISTING. You need to remember that the gateway will remember if * a node has presented it with a whitelisting requirement as described above, if you at some point * decide to remove the whitelist requirement.
- * The whitelist is provided as value of the flag that enable it as follows (example is a node that - * require signing as well): + * The whitelist is provided as value of the flag that enables it as follows (example is a node that + * requires signing as well): * @code{.cpp} * #define MY_SIGNING_ATSHA204 * #define MY_SIGNING_REQUEST_SIGNATURES @@ -289,8 +289,8 @@ * #define MY_SIGNING_SOFT_RANDOMSEED_PIN 7 * @endcode * Remember that you always need to select a signing backend for all nodes that communicate to a - * node that require whitelisting. Also, note that a node that use whitelisting will not accept - * messages from nodes that are not present in it's whitelist. + * node that requires whitelisting. Also, note that a node that uses whitelisting will not accept + * messages from nodes that are not present in its whitelist. * And you have to personalize all nodes that use signing with a common HMAC key but different * serial numbers (@ref MY_SIGNING_ATSHA204 always has unique serials). * @@ -312,7 +312,7 @@ * "Securely located" in this context mean a node which is not physically publicly accessible. * Typically at least your gateway.
* "Public" in this context mean a node that is located outside your "trusted environment". This - * includes sensors located outdoors, keyfobs etc. + * includes sensors located outdoors, keyfobs, etc. * * @subsection MySigninglock Securely located lock * @@ -338,7 +338,7 @@ * your gateway. You should lock the data (PSK) in this node then, because if someone were to steal * your patio motion sensor, they could rewrite the firmware and spoof your gateway to use it to * transmit a correctly signed message to your secure lock inside your house. But if you revoke your - * gateway (and lock) PSK the outside sensor cannot be used for this anymore. Nor can it be changed + * gateway (and lock) PSK the outside sensor cannot be used for this anymore, neither can it be changed * in order to do it in the future. You can also use whitelisting to revoke your lost node.
* This is an unlikely use case because there really is no reason to sign sensor values. If you for * some reason want to obfuscate sensor data, encryption is a better alternative.
@@ -364,7 +364,7 @@ * @subsection MySigningkeyfob Keyfob for garage door opener * - * Perhaps the most typical usecase for signed messages. Your keyfob should be totally locked down. + * This is perhaps the most typical use case for signed messages. Your keyfob should be totally locked down. * If the garage door opener is secured (and it should be) it can be unlocked. That way, if you * loose your keyfob, you can revoke the PSK in both the opener and your gateway, * thus rendering the keyfob useless without having to replace your nodes. You can also use @@ -415,7 +415,7 @@ * node. So it cannot be part of the signature, or the signature would be invalid when it arrives to * its destination. The signature also carries a byte with a signing identifier to prevent false * results from accidental mixing of incompatible signing backends in the network. Thus, the maximum - * size for a payload is 29-7 bytes. Larger payloads are not possible to sign at the moment. Another + * size for a payload is 29-7 bytes. Signing larger payloads is not supported at this time. Another * thing to consider is that the strength of the signature is inversely proportional to the payload * size. * @@ -430,7 +430,7 @@ * Exactly how this is done can be reviewed in the source for the software backend * (MySigningAtsha204Soft.cpp) and the ATSHA204A * datasheet - * . In the MySensors protocol, the following internal messagetypes handles signature + * . In the MySensors protocol, the following internal messagetypes handle signature * requirements and nonce requests:
* @ref I_SIGNING_PRESENTATION
* @ref I_NONCE_REQUEST
@@ -441,7 +441,7 @@ * * @section MySigninggrpbackground Background and concepts * - * Suppose two participants, Alice and Bob, wants to exchange a message. Alice sends a message to + * Suppose two participants, Alice and Bob, want to exchange a message. Alice sends a message to * Bob. In MySensors “language” Alice could be a gateway and Bob an actuator (light switch, * electronic lock, etc). But to be generic, we will substitute the term “gateway” with Alice and a * “node” with Bob (although the reverse relationship is also supported). @@ -453,7 +453,7 @@ * this is known as authenticity. Bob needs some way of determining that the message is * authentic from Alice, when Bob receives it. This prevents an eavesdropper, Eve, to trick Bob into * thinking it was Alice that sent a message Eve in fact transmitted. Bob also needs to know how to - * determine if the message has been repeated. Eve could record a message sent by Alice that Bob + * determine whether the message has been repeated. Eve could record a message sent by Alice that Bob * accepted and then send the same message again. Eve could also in some way prevent Bob from * receiving the message and delay it in order to permit the message to arrive to Bob at a time Eve * chooses, instead of Alice. Such an attack is known as a replay attack.
@@ -466,15 +466,15 @@ * keeping the parts that authenticate the message. That way, Bob still trusts Alice to be the * source, but the contents of the message was not the content Alice sent. Bob needs to be able to * determine that the contents of the message was not altered after Alice sent it.
- * Mallory would in this case be a man-in-the-middle attacker.
- * Integrity permits Bob to verify that the messages received from Alice has not been + * In this case, Mallory would be a man-in-the-middle attacker.
+ * Integrity permits Bob to verify that the message received from Alice has not been * tampered with.
* This is achieved by adding a signature to the message, which Bob can inspect to validate * that Alice is the author. * @image html MySigning/alicenfriends2.png * - * The signing scheme used, needs to address both these attack scenarios. Neither Eve nor Mallory - * must be permitted to interfere with the message exchange between Alice and Bob. + * The signing scheme used needs to address both these attack scenarios. Neither Eve nor Mallory + * may be permitted to interfere with the message exchange between Alice and Bob. * * The key challenge to implementing a secure signing scheme is to ensure that every signature is * different, even if the message is not. If not, replay attacks would be very hard to @@ -493,21 +493,21 @@ * * However, Mallory might be eavesdropping on the communication and snoop up the nonce in order to * generate a new valid signature for a different message. To counter this, both Alice and Bob keep - * a secret that only they know. This secret is never transmitted over the air, - * nor is it revealed to anybody. This secret is known as a + * a secret that only they know. This secret is never transmitted over the air + * or revealed to anybody. This secret is known as a * pre-shared key (PSK). * - * If Eve or Mallory are really sophisticated, he/she might use a delayed replay attack. + * If Eve or Mallory are really sophisticated, they might use a delayed replay attack. * This can be done by allowing Bob to transmit a nonce to Alice. But when Alice transmits the - * uniquely signed message, Mallory prevents Bob from receiving it, to a point when Mallory + * uniquely signed message, Mallory prevents Bob from receiving it, until a time when Mallory * decides Bob should receive it. An example of such an attack is described * * here.
* This needs to be addressed as well, and one way of doing this is to have Bob keep track of time * between a transmitted nonce and a signed message to verify. If Bob is asked for a nonce, Bob - * knows that a signed message is going to arrive “soon”. Bob can then decide that if the signed - * message does not arrive within a predefined timeframe, Bob throws away the generated nonce and - * thus makes it impossible to verify the message if it arrives late. + * knows that a signed message is going to arrive “soon”. Bob can then check that the signed + * message arrives within a predefined timeframe. If the message was sent after a delay, Bob has thrown away the + * generated nonce thus making it impossible to verify the message. * * The flow can be described like this: * @image html MySigning/alicenbob.png @@ -517,10 +517,10 @@ * @section MySigninggrphow How this is done * * There exist many forms of message signature solutions to combat Eve and Mallory.
- * Most of these solutions are quite complex in term of computations, so I elected to use an + * Most of these solutions are quite complex in terms of computation, so I elected to use an * algorithm that an external circuit is able to process. This has the added benefit of protecting * any keys and intermediate data used for calculating the signature so that even if someone were to - * actually steal a sensor and disassembled it, they would not be able to extract the keys and other + * actually steal a sensor and disassemble it, they would not be able to extract the keys and other * information from the device.
* A common scheme for message signing (authenticity and integrity) is implemented using * HMAC which in @@ -531,11 +531,11 @@ * is a (currently) virtually unbreakable combination. If SHA256 were to be hacked, a certain * cryptocurrency would immediately be rendered * worthless.
- * The ATSHA device also contain a random number generator (RNG) which enables the generation of a + * The ATSHA device also contains a random number generator (RNG) which enables the generation of a * good nonce, as in, non-predictable.
* As I acknowledge that some might not want to use an additional external circuit, I have also * implemented a software version of the ATSHA device, capable of generating the same signatures as - * the ATSHA device does. Because it is pure-software however, it does not provide as good nonces + * the ATSHA device does. Because it is pure-software, however, it does not provide as good nonces * (it uses the Arduino pseudo-random generator) * and the HMAC key is stored in SW and is therefore readable if the memory is dumped. It also * naturally claims more flash space due to the more complex software. But for indoor @@ -550,27 +550,27 @@ * reason is that a gateway and a node does not really care about messages being readable or not by * “others”. It makes more sense that such guarantees are provided by the underlying transmission * layer (RF solution in this case). It is the information transmitted over the air that needs to be - * secret (if user so desires). The “trust” level on the other hand needs to go all the way into the + * secret (if the user so desires). The “trust” level on the other hand needs to go all the way into the * sketches (who might have different requirements of trust depending on the message participant), * and for this reason, it is more important (and less complicated) to ensure authenticity and - * integrity at protocol-level as message contents is still readable throughout the protocol - * stack. But as soon as the message leaves the “stack” it can be scramble into “garbage” when + * integrity at the protocol-level as message content is still readable throughout the protocol + * stack. But as soon as the message leaves the “stack” it can be scrambled into “garbage” when * transmitted over the air and then reassembled by a receiving node before being fed in “the clear” * up the stack at the receiving end. * - * There are methods and possibilities to provide encryption also in software, but if this is done, - * it is my recommendation that this is done after integrity- and authentication information has + * There are also methods and possibilities to provide encryption in software, but if this is done, + * it is my recommendation that this is done after integrity and authentication information has * been provided to the message (if this is desired). Integrity and authentication is of course not * mandatory and some might be happy with only having encryption to cover their need for security. * I, however, have only focused on integrity and authenticity while at the same time - * keeping the current message routing mechanisms intact and therefore leave the matter of - * secrecy to be implemented in the “physical” transport layer. With the integrity and + * keeping the current message routing mechanisms intact and therefore leaving the matter of + * secrecy to be implemented in the “physical” transport layer. With integrity and * authenticity handled in the protocol it ought to be enough for a simple encryption - * (nonce-less %AES with a PSK for instance) on the message as it is sent to the RF backend. Atmel - * does provide such circuits as well but I have not investigated the matter further as it given the + * (nonce-less %AES with a PSK for instance) of the message as it is sent to the RF backend. Atmel + * does provide such circuits as well but I have not investigated the matter further given that the * current size of the ethernet gateway sketch is close to the size limit on an Arduino Nano, so it * will be difficult to fit this into some existing gateway designs.
- * Also it is worth to consider that the state of a lock can just as readily be determined by simply + * It is also worth considering that the state of a lock can just as readily be determined by simply * looking at the door in question or attempting to open it, so obfuscating this information will * not necessarily deter an attacker in any way.
* Nevertheless, I do acknowledge that people find the fact that all information is sent “in the @@ -625,13 +625,13 @@ void signerInit(void); * @brief Does signing specific presentation for a node. * * This function makes sure any signing related presentation info is shared with the other part. - * The presentation of the gateways signing preferences is done in @ref signerProcessInternal(). + * The presentation of the gateway's signing preferences is done in @ref signerProcessInternal(). * \n@b Usage: This function should be called by the presentation routine of the MySensors library. * You only need to call this directly from a sketch to set up a node to node signed message exchange. * If you do call this directly from a sketch, and you at some point change your sketch to go from - * requiring signing to not requiring signatures, you need to present this change to the node at least - * once, so it can update its requirements tables accordingly. Or it will keep believing that this node - * require signatures and attempt to send signed messages to it. + * requiring signing to not requiring signing, you need to present this change to the node at least + * once, so it can update its requirements tables accordingly, else it will keep believing that this node + * requires signatures and attempt to send signed messages to it. * * @param msg Message buffer to use. * @param destination Node ID of the destination. @@ -641,7 +641,7 @@ void signerPresentation(MyMessage &msg, uint8_t destination); /** * @brief Manages internal signing message handshaking. * - * This function takes care of signing related message handshaking such as nonce exchange. + * This function takes care of signing-related message handshaking such as nonce exchanges. * \n@b Usage: This function should be called by the incoming message handler before any further message * processing is performed on internal messages. This function should only be called for @ref C_INTERNAL class * messages. @@ -655,7 +655,7 @@ bool signerProcessInternal(MyMessage &msg); * @brief Check timeout of verification session. * * Nonce will be purged if it takes too long for a signed message to be sent to the receiver. - * \n@b Usage: This function should be called on regular intervals, typically within some process loop. + * \n@b Usage: This function should be called at regular intervals, typically within some process loop. * * @returns @c true if session is still valid. */ @@ -851,18 +851,18 @@ int signerMemcmp(const void* a, const void* b, size_t sz); * * The reason for this is that when signing is used, the messages transmitted become relatively large.
* Because of this, the message is more sensitive to noise, and the chance for a message to get scrambled - * increase with the message size. Please refer to the troubleshooting section at the MySensors forum for + * increases with the message size. Please refer to the troubleshooting section in the MySensors forum for * information on how to improve radio performance.
* This is a good place to start: https://forum.mysensors.org/topic/666/debug-faq-and-how-ask-for-help * * @subsection MySigningTroubleshootingSymptomNonce Failed to generate nonce * - * The signing backend failed to generate the nonce needed to sign a message. This indicate a hardware + * The signing backend failed to generate the nonce needed to sign a message. This indicates a hardware * problem. Please post the debug info on the forum together with a description of your hardware setup. * * @subsection MySigningTroubleshootingSymptomSign Failed to sign message * - * The signing backend failed to sign the message. Typically this happens if your message is so large, + * The signing backend failed to sign the message. Typically this happens if your message is so large * that there is no room left in the buffer to store a signature. * * @subsection MySigningTroubleshootingSymptomWrongSource Nonce did not come from the destination (XX) of the message to be signed! It came from YY @@ -872,8 +872,8 @@ int signerMemcmp(const void* a, const void* b, size_t sz); * the same node, only the nonce from a node that is the destination of the current message signing session will be * accepted. Any other nonces will be dropped. This should not happen as no node should send a nonce unless asked to, * and a node will only ask for a nonce to one destination for every signing session.
- * If you see this message, please post the debugging details on the MySensors forum so it can be investigated further - * together with a description of your setup. + * If you see this message, please post the debugging details (with a description of your setup) on the MySensors + * forum so it can be investigated further. * * @subsection MySigningTroubleshootingSymptomNotSigned Message is not signed, but it should have been * diff --git a/core/MyTransport.cpp b/core/MyTransport.cpp index 77193ba8d..7a26ffa7b 100644 --- a/core/MyTransport.cpp +++ b/core/MyTransport.cpp @@ -546,15 +546,17 @@ bool transportRouteMessage(MyMessage &message) #endif } #else - if (destination > GATEWAY_ADDRESS && destination < BROADCAST_ADDRESS) { - // node2node traffic: assume node is in vincinity. If transmission fails, hand over to parent + // not a repeater, all traffic routed via parent or N2N + route = _transportConfig.parentNodeId; + // Try node2node traffic if destination is not parent and is not a broadcast + if (destination != route && destination != BROADCAST_ADDRESS) { + // N2N: assume node is in vicinity. If transmission fails, hand over to parent if (transportSendWrite(destination, message)) { TRANSPORT_DEBUG(PSTR("TSF:RTE:N2N OK\n")); return true; } TRANSPORT_DEBUG(PSTR("!TSF:RTE:N2N FAIL\n")); } - route = _transportConfig.parentNodeId; // not a repeater, all traffic routed via parent #endif } // send message diff --git a/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino b/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino index 842c61483..bf33dc26a 100644 --- a/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino +++ b/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino @@ -68,12 +68,6 @@ MyMessage wattMsg(CHILD_ID, V_WATT); MyMessage kWhMsg(CHILD_ID, V_KWH); MyMessage pcMsg(CHILD_ID, V_VAR1); -#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) -#define IRQ_HANDLER_ATTR ICACHE_RAM_ATTR -#else -#define IRQ_HANDLER_ATTR -#endif - void IRQ_HANDLER_ATTR onPulse() { if (!SLEEP_MODE) { diff --git a/examples/GatewayESP8266SecureMQTTClient/GatewayESP8266SecureMQTTClient.ino b/examples/GatewayESP8266SecureMQTTClient/GatewayESP8266SecureMQTTClient.ino new file mode 100644 index 000000000..531adb57d --- /dev/null +++ b/examples/GatewayESP8266SecureMQTTClient/GatewayESP8266SecureMQTTClient.ino @@ -0,0 +1,190 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2019 Sensnology AB + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + ******************************* + * + * REVISION HISTORY + * Version 1.0 - Henrik Ekblad + * + * DESCRIPTION + * The ESP8266 MQTT gateway sends radio network (or locally attached sensors) data to your MQTT broker. + * The node also listens to MY_MQTT_TOPIC_PREFIX and sends out those messages to the radio network + * + * LED purposes: + * - To use the feature, uncomment any of the MY_DEFAULT_xx_LED_PINs in your sketch + * - RX (green) - blink fast on radio message received. In inclusion mode will blink fast only on presentation received + * - TX (yellow) - blink fast on radio message transmitted. In inclusion mode will blink slowly + * - ERR (red) - fast blink on error during transmission error or receive crc error + * + * See https://www.mysensors.org/build/connect_radio for wiring instructions. + * + * If you are using a "barebone" ESP8266, see + * https://www.mysensors.org/build/esp8266_gateway#wiring-for-barebone-esp8266 + * + * Inclusion mode button: + * - Connect GPIO5 (=D1) via switch to GND ('inclusion switch') + * + * Hardware SHA204 signing is currently not supported! + * + * Make sure to fill in your ssid and WiFi password below for ssid & pass. + * + ******************************** + * + * SSL support by Eric Grammatico. You should have an updated version of MyGatewayTransportMQTTClient.cpp. + * Please see: https://forum.mysensors.org/topic/11941/esp8266-mqtt-gateway-ssl-connection + * + * The following constants have to be defined from the gateway code: + * MY_GATEWAY_ESP8266_SECURE In place of MY_GATEWAY_ESP8266 to go to secure connexions. + * MY_MQTT_CA_CERTx Up to three root Certificates Authorities could be defined + * to validate the mqtt server' certificate. The most secure. + * MY_MQTT_CA_CERT is deprecated and MY_MQTT_CA_CERT1 should + * be used instead. + * MY_MQTT_FINGERPRINT Alternatively, the mqtt server' certificate finger print + * could be used. Less secure and less convenient as you'll + * have to update the fingerprint each time the mqtt server' + * certificate is updated + * If neither MY_MQTT_CA_CERT1 nor MY_MQTT_FINGERPRINT are + * defined, insecure connexion will be established. The mqtt + * server' certificate will not be validated. + * MY_MQTT_CLIENT_CERT The mqtt server may require client certificate for + * MY_MQTT_CLIENT_KEY authentication. + * + * The certs.h file holds the mqtt server' fingerprint and root Certificate Authorities and + * client certificate and key. This a sample how to populate MY_MQTT_CA_CERTx, MY_MQTT_FINGERPRINT, + * MY_MQTT_CLIENT_CERT and MY_MQTT_CLIENT_KEY. + */ + +// Imports certificates and client key +#include "certs.h" + +/********************************** + * MySensors node configuration + */ + +// General settings +#define SKETCH_NAME "MySensorsMQTTGW_Secure" +#define SKETCH_VERSION "0.6" +#define MY_DEBUG +#define MY_NODE_ID 1 + +// Use a bit lower baudrate for serial prints on ESP8266 than default in MyConfig.h +#define MY_BAUD_RATE 9600 + +// Enables and select radio type (if attached) +#define MY_RADIO_RF24 +//#define MY_RF24_PA_LEVEL RF24_PA_LOW + +//#define MY_RADIO_RFM69 +//#define MY_RADIO_RFM95 + +/************** + * Secured connexion with ESP8266 + */ +#define MY_GATEWAY_ESP8266_SECURE +//** Set WIFI SSID and password +#define MY_WIFI_SSID "ssid" +#define MY_WIFI_PASSWORD "password" +//** Set the hostname for the WiFi Client. This is the hostname +// passed to the DHCP server if not static. +#define MY_HOSTNAME "esp8266-gw" +// Enable MY_IP_ADDRESS here if you want a static ip address (no DHCP) +//#define MY_IP_ADDRESS 192,168,178,87 + +// If using static ip you can define Gateway and Subnet address as well +//#define MY_IP_GATEWAY_ADDRESS 192,168,178,1 +//#define MY_IP_SUBNET_ADDRESS 255,255,255,0 + +//** Certificate Authorities. One or two should be enough +#define MY_MQTT_CA_CERT1 cert_isrgrootx1_Authority +#define MY_MQTT_CA_CERT2 cert_isrgrootx2_Authority +//#define MY_MQTT_CA_CERT3 cert_letsEncryptR3_Authority + +//** Server certificate validation with its fingerprint +// less secure and less convenient than with Certificate +// Authorities as server certificates are updated often. +// Will not be used if MY_MQTT_CA_CERT1 defined. +#define MY_MQTT_FINGERPRINT mqtt_fingerprint + +//** The mqtt server may require client certificate for +// authentication. +#define MY_MQTT_CLIENT_CERT cert_client +#define MY_MQTT_CLIENT_KEY key_client + + +/************** + * MQTT_CLIENT configuration + */ +#define MY_GATEWAY_MQTT_CLIENT + +//** MQTT broker if using URL instead of ip address. +// should correspond to the CN field in the mqtt server' +// certificate. +#define MY_CONTROLLER_URL_ADDRESS mqtt_host + +//** The MQTT broker port to open +#define MY_PORT mqtt_port + +//** Enable these if your MQTT broker requires username/password +//#define MY_MQTT_USER "" +//#define MY_MQTT_PASSWORD "" +//** Set MQTT client id +//#define MY_MQTT_CLIENT_ID "" + +//** Set this node's subscribe and publish topic prefix +#define MY_MQTT_PUBLISH_TOPIC_PREFIX "esp8266-gw/out" +#define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "esp8266-gw/in" + + +// Enable inclusion mode +//#define MY_INCLUSION_MODE_FEATURE +// Enable Inclusion mode button on gateway +//#define MY_INCLUSION_BUTTON_FEATURE +// Set inclusion mode duration (in seconds) +//#define MY_INCLUSION_MODE_DURATION 60 +// Digital pin used for inclusion mode button +//#define MY_INCLUSION_MODE_BUTTON_PIN D1 + +// Set blinking period +//#define MY_DEFAULT_LED_BLINK_PERIOD 300 + +// Flash leds on rx/tx/err +//#define MY_DEFAULT_ERR_LED_PIN 16 // Error led pin +//#define MY_DEFAULT_RX_LED_PIN 16 // Receive led pin +//#define MY_DEFAULT_TX_LED_PIN 16 // the PCB, on board LED + +#include + +void setup() +{ + + // In order to speed up certs and keys verifications + system_update_cpu_freq(160); + + // Setup locally attached sensors +} + +void presentation() +{ + // Present locally attached sensors here +} + +void loop() +{ + // Send locally attached sensors data here +} + diff --git a/examples/GatewayESP8266SecureMQTTClient/certs.h b/examples/GatewayESP8266SecureMQTTClient/certs.h new file mode 100644 index 000000000..443fd2821 --- /dev/null +++ b/examples/GatewayESP8266SecureMQTTClient/certs.h @@ -0,0 +1,148 @@ +#pragma once + +// The mqqt host and port +const char* mqtt_host = ""; +const uint16_t mqtt_port = 8883; //Should be your mqtt broker' port + +/* +*The finger print to validate the mqtt server certificate. This is less secure and less convenient +* than using certificate authorities +* Command (3 lines...) to obtain the certificate finger print: +* $>openssl s_client -connect : < /dev/null 2>/dev/null | \ +* openssl x509 -fingerprint -noout -in /dev/stdin \ +* awk -F= '{print $2}' +*/ +const char mqtt_fingerprint [] PROGMEM = + "CA:EE:2B:ED:D3:23:A7:F1:8C:73:9E:9B:B7:D5:75:41:10:61:E4:05"; + +/// @cond RAW_LITERALS +// Doxygen doesn't seem to like raw literals + +/* +*Certificate Authorities. The best method to validate server certificates +* Advised to retrieve root Certificate Authorities as they expire less often +* than server certificates. Here after letsencrypt Certificate Authorities are listed. +* They are available at https://letsencrypt.org/certificates/ +* +* Root Certificate Authorities ISRG Root X1 +* https://letsencrypt.org/certs/isrgrootx1.pem +* Not Before: Jun 4 11:04:38 2015 GMT +* Not After : Jun 4 11:04:38 2035 GMT +*/ +const char cert_isrgrootx1_Authority [] PROGMEM = R"CERT( +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +)CERT"; + +/* +* Root Certificate Authorities ISRG Root X2 +* https://letsencrypt.org/certs/isrg-root-x2.pem +* Not Before: Sep 4 00:00:00 2020 GMT +* Not After : Sep 17 16:00:00 2040 GMT +*/ +const char cert_isrgrootx2_Authority [] PROGMEM = R"CERT( +-----BEGIN CERTIFICATE----- +MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw +CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg +R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 +MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT +ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW ++1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 +ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI +zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW +tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 +/q4AaOeMSQ+2b1tbFfLn +-----END CERTIFICATE----- +)CERT"; + +/* +* This one shouldn't be needed.... +* Root Certificate Authorities Let’s Encrypt R3 +* https://letsencrypt.org/certs/lets-encrypt-r3.pem +* Not Before: Sep 4 00:00:00 2020 GMT +* Not After : Sep 15 16:00:00 2025 GMT +*/ +const char cert_letsEncryptR3_Authority [] PROGMEM = R"CERT( +-----BEGIN CERTIFICATE----- +MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw +WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg +RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP +R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx +sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm +NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg +Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG +/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB +Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA +FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw +AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw +Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB +gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W +PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl +ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz +CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm +lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4 +avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2 +yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O +yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids +hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+ +HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv +MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX +nLRbwHOoq7hHwg== +-----END CERTIFICATE----- +)CERT"; + +/* +*The mqtt server may require client certificate for authentication. +* The following are genereted and signed thanks to openssl. +* The signing certificate is holded by the mqtt server. +* Please see Client section in https://mosquitto.org/man/mosquitto-tls-7.html +*/ +const char cert_client [] PROGMEM = R"CERT( +-----BEGIN CERTIFICATE----- +... ... ... ... ... +-----END CERTIFICATE----- +)CERT"; + +const char key_client [] PROGMEM = R"CERT( +-----BEGIN RSA PRIVATE KEY----- +... ... ... ... ... +-----END RSA PRIVATE KEY----- +)CERT"; +// end of certificate chain + +/// @endcond diff --git a/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino b/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino index ca3d56879..2493037f1 100644 --- a/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino +++ b/examples/WaterMeterPulseSensor/WaterMeterPulseSensor.ino @@ -75,12 +75,6 @@ double oldvolume =0; uint32_t lastSend =0; uint32_t lastPulse =0; -#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) -#define IRQ_HANDLER_ATTR ICACHE_RAM_ATTR -#else -#define IRQ_HANDLER_ATTR -#endif - void IRQ_HANDLER_ATTR onPulse() { if (!SLEEP_MODE) { diff --git a/hal/architecture/ESP8266/MyMainESP8266.cpp b/hal/architecture/ESP8266/MyMainESP8266.cpp index 8b3c4283c..70a2e1006 100644 --- a/hal/architecture/ESP8266/MyMainESP8266.cpp +++ b/hal/architecture/ESP8266/MyMainESP8266.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2020 Sensnology AB + * Copyright (C) 2013-2022 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -17,332 +17,29 @@ * version 2 as published by the Free Software Foundation. */ -//This may be used to change user task stack size: -//#define CONT_STACKSIZE 4096 -#include -#include "Schedule.h" -extern "C" { -#include "ets_sys.h" -#include "os_type.h" -#include "osapi.h" -#include "mem.h" -#include "user_interface.h" -#include "cont.h" -} -#include -#include "gdb_hooks.h" - -#define LOOP_TASK_PRIORITY 1 -#define LOOP_QUEUE_SIZE 1 -#define OPTIMISTIC_YIELD_TIME_US 16000 - -extern "C" void call_user_start(); -extern void loop(); -extern void setup(); -extern void(*__init_array_start)(void); -extern void(*__init_array_end)(void); - -/* Not static, used in Esp.cpp */ -struct rst_info resetInfo; - -/* Not static, used in core_esp8266_postmortem.c and other places. - * Placed into noinit section because we assign value to this variable - * before .bss is zero-filled, and need to preserve the value. - */ -cont_t* g_pcont __attribute__((section(".noinit"))); - -/* Event queue used by the main (arduino) task */ -static os_event_t s_loop_queue[LOOP_QUEUE_SIZE]; - -/* Used to implement optimistic_yield */ -static uint32_t s_micros_at_task_start; - -/* For ets_intr_lock_nest / ets_intr_unlock_nest - * Max nesting seen by SDK so far is 2. - */ -#define ETS_INTR_LOCK_NEST_MAX 7 -static uint16_t ets_intr_lock_stack[ETS_INTR_LOCK_NEST_MAX]; -static byte ets_intr_lock_stack_ptr = 0; - - -extern "C" { - extern const uint32_t __attribute__((section(".ver_number"))) core_version = - ARDUINO_ESP8266_GIT_VER; - const char* core_release = -#ifdef ARDUINO_ESP8266_RELEASE - ARDUINO_ESP8266_RELEASE; -#else - NULL; -#endif -} // extern "C" - -void initVariant() __attribute__((weak)); -void initVariant() -{ -} - -void preloop_update_frequency() __attribute__((weak)); -void preloop_update_frequency() -{ -#if defined(F_CPU) && (F_CPU == 160000000L) - REG_SET_BIT(0x3ff00014, BIT(0)); - ets_update_cpu_frequency(160); -#endif -} - -extern "C" bool can_yield() -{ - return cont_can_yield(g_pcont); -} - -static inline void esp_yield_within_cont() __attribute__((always_inline)); -static void esp_yield_within_cont() -{ - cont_yield(g_pcont); - run_scheduled_recurrent_functions(); -} - -extern "C" void esp_yield() -{ - if (can_yield()) { - esp_yield_within_cont(); - } -} - -extern "C" void esp_schedule() -{ - // always on CONT stack here - ets_post(LOOP_TASK_PRIORITY, 0, 0); -} - -extern "C" void __yield() -{ - if (can_yield()) { - esp_schedule(); - esp_yield_within_cont(); - } else { - panic(); - } -} - -extern "C" void yield(void) __attribute__((weak, alias("__yield"))); - -extern "C" void optimistic_yield(uint32_t interval_us) -{ - if (can_yield() && - (system_get_time() - s_micros_at_task_start) > interval_us) { - yield(); - } -} - - -// Replace ets_intr_(un)lock with nestable versions -extern "C" void IRAM_ATTR ets_intr_lock() -{ - if (ets_intr_lock_stack_ptr < ETS_INTR_LOCK_NEST_MAX) { - ets_intr_lock_stack[ets_intr_lock_stack_ptr++] = xt_rsil(3); - } else { - xt_rsil(3); - } -} - -extern "C" void IRAM_ATTR ets_intr_unlock() -{ - if (ets_intr_lock_stack_ptr > 0) { - xt_wsr_ps(ets_intr_lock_stack[--ets_intr_lock_stack_ptr]); - } else { - xt_rsil(0); - } -} - - -// Save / Restore the PS state across the rom ets_post call as the rom code -// does not implement this correctly. -extern "C" bool ets_post_rom(uint8 prio, ETSSignal sig, ETSParam par); - -extern "C" bool IRAM_ATTR ets_post(uint8 prio, ETSSignal sig, ETSParam par) -{ - uint32_t saved; - asm volatile ("rsr %0,ps":"=a" (saved)); - bool rc = ets_post_rom(prio, sig, par); - xt_wsr_ps(saved); - return rc; -} - -extern "C" void __loop_end(void) +inline void _my_sensors_loop() { - run_scheduled_functions(); - run_scheduled_recurrent_functions(); -} - -extern "C" void loop_end(void) __attribute__((weak, alias("__loop_end"))); - -static void loop_wrapper() -{ - static bool setup_done = false; - preloop_update_frequency(); - if (!setup_done) { - _begin(); // Startup MySensors library - setup_done = true; - } - _process(); // Process incoming data + // Process incoming data + _process(); + // Call of loop() in the Arduino sketch loop(); - run_scheduled_functions(); - esp_schedule(); } -static void loop_task(os_event_t *events) -{ - (void)events; - s_micros_at_task_start = system_get_time(); - cont_run(g_pcont, &loop_wrapper); - if (cont_check(g_pcont) != 0) { - panic(); - } -} -extern "C" { - - struct object { - long placeholder[10]; - }; - void __register_frame_info(const void *begin, struct object *ob); - extern char __eh_frame[]; -} - -static void do_global_ctors(void) -{ - static struct object ob; - __register_frame_info(__eh_frame, &ob); - - void(**p)(void) = &__init_array_end; - while (p != &__init_array_start) { - (*--p)(); - } -} - -extern "C" { - extern void __unhandled_exception(const char *str); - - static void __unhandled_exception_cpp() - { -#ifndef __EXCEPTIONS - abort(); -#else - static bool terminating; - if (terminating) { - abort(); - } - terminating = true; - /* Use a trick from vterminate.cc to get any std::exception what() */ - try { - __throw_exception_again; - } catch (const std::exception& e) { - __unhandled_exception(e.what()); - } catch (...) { - __unhandled_exception(""); - } -#endif - } - -} - -void init_done() -{ - system_set_os_print(1); - gdb_init(); - std::set_terminate(__unhandled_exception_cpp); - do_global_ctors(); - esp_schedule(); -} - -/* This is the entry point of the application. - * It gets called on the default stack, which grows down from the top - * of DRAM area. - * .bss has not been zeroed out yet, but .data and .rodata are in place. - * Cache is not enabled, so only ROM and IRAM functions can be called. - * Peripherals (except for SPI0 and UART0) are not initialized. - * This function does not return. - */ /* - A bit of explanation for this entry point: - - SYS is the SDK task/context used by the upperlying system to run its - administrative tasks (at least WLAN and lwip's receive callbacks and - Ticker). NONOS-SDK is designed to run user's non-threaded code in - another specific task/context with its own stack in BSS. - - Some clever fellows found that the SYS stack was a large and quite unused - piece of ram that we could use for the user's stack instead of using user's - main memory, thus saving around 4KB on ram/heap. - - A problem arose later, which is that this stack can heavily be used by - the SDK for some features. One of these features is WPS. We still don't - know if other features are using this, or if this memory is going to be - used in future SDK releases. - - WPS beeing flawed by its poor security, or not beeing used by lots of - users, it has been decided that we are still going to use that memory for - user's stack and disable the use of WPS. - - app_entry() jumps to app_entry_custom() defined as "weakref" calling - itself a weak customizable function, allowing to use another one when - this is required (see core_esp8266_app_entry_noextra4k.cpp, used by WPS). - - (note: setting app_entry() itself as "weak" is not sufficient and always - ends up with the other "noextra4k" one linked, maybe because it has a - default ENTRY(app_entry) value in linker scripts). - - References: - https://github.com/esp8266/Arduino/pull/4553 - https://github.com/esp8266/Arduino/pull/4622 - https://github.com/esp8266/Arduino/issues/4779 - https://github.com/esp8266/Arduino/pull/4889 - -*/ - -extern "C" void app_entry_redefinable(void) __attribute__((weak)); -extern "C" void app_entry_redefinable(void) -{ - /* Allocate continuation context on this SYS stack, - and save pointer to it. */ - cont_t s_cont __attribute__((aligned(16))); - g_pcont = &s_cont; - - /* Call the entry point of the SDK code. */ - call_user_start(); -} - -static void app_entry_custom(void) __attribute__((weakref("app_entry_redefinable"))); - -extern "C" void app_entry(void) -{ - return app_entry_custom(); -} - -extern "C" void preinit(void) __attribute__((weak)); -extern "C" void preinit(void) -{ - /* do nothing by default */ -} - -extern "C" void user_init(void) -{ - struct rst_info *rtc_info_ptr = system_get_rst_info(); - memcpy((void *)&resetInfo, (void *)rtc_info_ptr, sizeof(resetInfo)); - - uart_div_modify(0, UART_CLK_FREQ / (115200)); - - init(); // in core_esp8266_wiring.c, inits hw regs and sdk timer - - initVariant(); - - cont_init(g_pcont); + * Use preprocessor defines for injection of the MySensors calls + * to _begin() and _process() in file core_esp8266_main.cpp. + * These functions implement the "magic" how the MySensors stack + * is setup and executed in background without need + * for explicit calls from the Arduino sketch. + */ - preinit(); // Prior to C++ Dynamic Init (not related to above init() ). Meant to be user redefinable. +// Start up MySensors library including call of setup() in the Arduino sketch +#define setup _begin +// Helper function to _process() and call of loop() in the Arduino sketch +#define loop _my_sensors_loop - ets_task(loop_task, - LOOP_TASK_PRIORITY, s_loop_queue, - LOOP_QUEUE_SIZE); +#include - system_init_done_cb(&init_done); -} +// Tidy up injection defines +#undef loop +#undef setup diff --git a/hal/architecture/MyHwHAL.h b/hal/architecture/MyHwHAL.h index 6df861c0f..2c485e170 100644 --- a/hal/architecture/MyHwHAL.h +++ b/hal/architecture/MyHwHAL.h @@ -43,7 +43,11 @@ * @brief ESP8266/ESP32 IRQ handlers need to be stored in IRAM */ #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) +#if defined(ARDUINO_ARCH_ESP8266) && ARDUINO_ESP8266_MAJOR == 3 +#define IRQ_HANDLER_ATTR IRAM_ATTR +#else #define IRQ_HANDLER_ATTR ICACHE_RAM_ATTR +#endif #else #define IRQ_HANDLER_ATTR #endif diff --git a/hal/transport/PJON/MyTransportPJON.cpp b/hal/transport/PJON/MyTransportPJON.cpp index 0a0e5e2c7..52dc59582 100644 --- a/hal/transport/PJON/MyTransportPJON.cpp +++ b/hal/transport/PJON/MyTransportPJON.cpp @@ -22,7 +22,10 @@ */ // Set a higher polling duration if the device is executing long tasks + +#ifndef PJON_POLLING_DURATION #define PJON_POLLING_DURATION 1000 +#endif PJONSoftwareBitBang bus; diff --git a/hal/transport/PJON/driver/PJONDefines.h b/hal/transport/PJON/driver/PJONDefines.h index 6c7ffc359..744836b5e 100644 --- a/hal/transport/PJON/driver/PJONDefines.h +++ b/hal/transport/PJON/driver/PJONDefines.h @@ -412,8 +412,9 @@ struct PJONTools { static void parse_header(const uint8_t *packet, PJON_Packet_Info &info) { - memset(&info, 0, sizeof info); uint8_t index = 0; + info = PJON_Packet_Info{}; + info.rx.id = packet[index++]; bool extended_length = packet[index] & PJON_EXT_LEN_BIT; info.header = packet[index++]; diff --git a/keywords.txt b/keywords.txt index 26bfc2f2c..deedf7e36 100755 --- a/keywords.txt +++ b/keywords.txt @@ -255,16 +255,21 @@ MY_GATEWAY_CLIENT_MODE LITERAL1 MY_GATEWAY_ENC28J60 LITERAL1 MY_GATEWAY_ESP32 LITERAL1 MY_GATEWAY_ESP8266 LITERAL1 +MY_GATEWAY_ESP8266_SECURE LITERAL1 MY_GATEWAY_MQTT_CLIENT LITERAL1 MY_GATEWAY_SERIAL LITERAL1 MY_GATEWAY_W5100 LITERAL1 MY_HOSTNAME LITERAL1 MY_INCLUSION_BUTTON_EXTERNAL_PULLUP LITERAL1 -MY_MQTT_CA_CERT LITERAL1 +MY_MQTT_CA_CERT LITERAL1 +MY_MQTT_CA_CERT1 LITERAL1 +MY_MQTT_CA_CERT2 LITERAL1 +MY_MQTT_CA_CERT3 LITERAL1 MY_MQTT_CLIENT_CERT LITERAL1 MY_MQTT_CLIENT_ID LITERAL1 MY_MQTT_CLIENT_KEY LITERAL1 MY_MQTT_CLIENT_PUBLISH_RETAIN LITERAL1 +MY_MQTT_FINGERPRINT LITERAL1 MY_MQTT_PASSWORD LITERAL1 MY_MQTT_PUBLISH_TOPIC_PREFIX LITERAL1 MY_MQTT_SUBSCRIBE_TOPIC_PREFIX LITERAL1