From f98474bed0b10db4aa0d9a35f9a3786a7dcbff6e Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 20 May 2024 09:30:49 -0500 Subject: [PATCH] [SYS] Multiple connections management (#1947) Enable the management of different connections at runtime with a set of certs for each. Add parameters to the WiFi Manager onboarding, client certificates, and certificate validation --- .github/workflows/build.yml | 1 - docs/upload/advanced-configuration.md | 18 +- docs/upload/portal.md | 6 +- docs/use/gateway.md | 134 +++-- environments.ini | 16 +- main/User_config.h | 221 ++++---- main/Zblufi.ino | 62 ++- main/ZgatewayBT.ino | 4 +- main/ZgatewayIR.ino | 8 +- main/ZgatewayRF.ino | 10 +- main/ZmqttDiscovery.ino | 2 + main/ZwebUI.ino | 15 +- main/main.ino | 758 +++++++++++++++++--------- platformio.ini | 1 - 14 files changed, 808 insertions(+), 448 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f6d1a5bd..2d65c5e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,7 +62,6 @@ jobs: - "nodemcuv2-rf-cc1101" - "nodemcuv2-somfy-cc1101" - "manual-wifi-test" - - "nodemcuv2-mqtt-fw-test" - "rf-wifi-gateway" - "nodemcuv2-rf2" - "nodemcuv2-rf2-cc1101" diff --git a/docs/upload/advanced-configuration.md b/docs/upload/advanced-configuration.md index f6ea523c..fee36a71 100644 --- a/docs/upload/advanced-configuration.md +++ b/docs/upload/advanced-configuration.md @@ -10,16 +10,17 @@ The configuration of the broker is not covered here, you should look into the do ### Prerequisites The MQTT broker is configured for TLS and you have access to the CA certificate which was used to sign the MQTT broker certificate. -You are using ESP8266 or ESP32, for other boards TLS is not supported. +You are using ESP8266 or ESP32. ### Configure secure connection in the gateway -To enable the secure connection and use TLS set the `#define MQTT_DEFAULT_SECURE` to true. +To enable the secure connection and use TLS set the `#define MQTT_DEFAULT_SECURE` to true at build time, or the `mqtt secure` parameter with WiFi Manager or `mqtt_secure` with MQTT. Set `MQTT_SERVER` to the Common Name (CN) of the certificate of the broker. -This can be the hostname or the IP of the broker. + +The server identity can be verified against a certificate or not, if you don't want to use a certicate to verify the server you can uncheck the option `validate cert` with WiFi Manager or set `mqtt_validate` to `false` with MQTT The CA certificate should be in PEM ascii format. If your CA certificate is not in the correct format or you don't know the format, use `openssl` to convert the certificate to the correct format. -In `User_config.h` replace the `...` with the content of your certificate which is between the `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` lines: +At build time, in `main/certs/default_server_cert.h` replace the `...` with the content of your certificate which is between the `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` lines: ```cpp const char* certificate CERT_ATTRIBUTE = R"EOF(" -----BEGIN CERTIFICATE----- @@ -28,7 +29,14 @@ const char* certificate CERT_ATTRIBUTE = R"EOF(" ")EOF"; ``` -You can know compile and upload to your board and the gateway should connect with TLS to your broker. +With WiFi Manager copy your certificate from `-----BEGIN CERTIFICATE-----` to `-----END CERTIFICATE-----` (included) and paste it into the input field: +``` +-----BEGIN CERTIFICATE----- +... +-----END CERTIFICATE----- +``` + +This process can also be used for the other certificates, OTA, client key, client certificate if necessary. ## Add the received "value" at the end of the topic For the gateways that publish a "value" parameter on the json (RF, IR...), it is possible to concatenate this parameter at the end of the topic. diff --git a/docs/upload/portal.md b/docs/upload/portal.md index 2e20d6c4..1e9140e5 100644 --- a/docs/upload/portal.md +++ b/docs/upload/portal.md @@ -29,8 +29,6 @@ If the board is connected by ethernet, the Wifi and password can be empty. If yo * Set your MQTT Server IP or domain name (for Home Assistant you can enter `homeassistant.local` if your server is configured with mDNS) * Set your MQTT Server Port (default: 1883) -* Set the MQTT secure connection box to select whether or not the connection should be secure -* Copy/paste the MQTT server certificate in the MQTT server cert box (only required if using a secure connection), be sure to include the `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` markers * Set your MQTT Server username (optional, 64 characters maximum) * Set your MQTT Server password (optional, 64 characters maximum) * Set your MQTT base topic if you need to change it (you must keep the / at the end) (default: home/, 64 characters maximum) @@ -39,6 +37,10 @@ If the board is connected by ethernet, the Wifi and password can be empty. If yo * Local and remote OTA * WebUI login (login:"admin", password: "") * WiFi Manager Access Point password after initial config +* Set the MQTT secure connection box to select whether or not the connection should be secure +* Set the certificate validation to select wether or not you want to validate the server identity with a certificate +* Copy/paste the MQTT server certificate in the MQTT server cert box (only required if using a secure connection), be sure to include the `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` markers +* Add your certificates * Click on save ![WiFi manager save](../img/OpenMQTTGateway_Wifi_Manager_save.png) diff --git a/docs/use/gateway.md b/docs/use/gateway.md index 08150608..13224922 100644 --- a/docs/use/gateway.md +++ b/docs/use/gateway.md @@ -69,9 +69,11 @@ mosquitto_pub -t "home/OpenMQTTGateway/commands/MQTTtoSYS/config" -m "mqtt_pass": "password", "mqtt_server": "host", "mqtt_port": "port", - "mqtt_secure": "false" + "mqtt_validate": false, + "mqtt_secure": false }' ``` + ::: tip INFO By default this function is not available on the pre built binary of RFBridge, in order to have less code size and enable to have OTA update working properly. So as to enable it remove from the rf bridge env: ``` @@ -80,7 +82,6 @@ build_flags = '-UMQTTsetMQTT' ::: ::: tip -Server, port, and secure_flag are only required if changing connection to another broker. If the new connection fails the gateway will fallback to the previous connection. ::: @@ -96,13 +97,13 @@ mosquitto_pub -t "home/OpenMQTTGateway/commands/MQTTtoSYS/config" -m This will change the subscribed and published mqtt_topic/gateway_name that the gateway uses. No parameters are mandatory, the current topic or gateway name will be used if not supplied. ::: -## Switching brokers and using self signed and client certificates +## Switching brokers and using signed and client certificates -In the `user_config.h` file it is possible to specify multiple MQTT brokers and client certificates. These are commonly self signed and are supported by defining `MQTT_SECURE_SELF_SIGNED` as true or 1. +In the `user_config.h` file it is possible to specify multiple MQTT brokers and client certificates. Additionally, support for multiple brokers and client certificates has been added. To use this, it is required that the server certificate, client certificate, and client key are provided as their own constant string value as demonstrated in the file. -To add more than one broker and switch between them it is necessary to provide all of the relevant certificates/keys and add their respective variable names in the `certs_array` structure, as shown in `user_config.h`, and changing the array size to the number of different connections -1. +To add more than one broker and switch between them it is necessary to provide all of the relevant certificates/keys and add their respective variable names in the `cnt_parameters_array` structure, as shown in `user_config.h`.. -To switch between these servers with an MQTT command message, the format is as follows: +To switch between these connections with an MQTT command message, the format is as follows: ``` mosquitto_pub -t "home/OpenMQTTGateway/commands/MQTTtoSYS/config" -m '{ @@ -110,14 +111,89 @@ mosquitto_pub -t "home/OpenMQTTGateway/commands/MQTTtoSYS/config" -m "mqtt_pass": "password", "mqtt_server": "host", "mqtt_port": "port", - "mqtt_secure": "true", - "mqtt_cert_index":0 + "mqtt_secure": true, + "mqtt_validate": true, + "cnt_index":1, + "save_cnt": true }' ``` ::: tip -The `mqtt_cert_index` value corresponds to the 0 to X index of the `certs_array` in `user_config.h`. +The `cnt_index` value corresponds to the 0 to 2 index of the `cnt_parameters_array` in `user_config.h`. +0 being the default index, containing the onboarding parameters. ::: +To read the connection parameters: +``` +mosquitto_pub -t "home/OpenMQTTGateway/commands/MQTTtoSYS/config" -m +'{ + "cnt_index":1, + "read_cnt": true + }' + ``` + + To test a connection change without saving: + ``` +mosquitto_pub -t "home/OpenMQTTGateway/commands/MQTTtoSYS/config" -m +'{ + "cnt_index":1, + "test_cnt": true + }' + ``` + +## Saving/Loading connection parameters/certificates at runtime +This chapter details the process for managing certificates/connections parameters used for secure MQTT communication with OpenMQTTGateway + +### Storing and Loading Certificates +* Flash Memory Storage: +Certificates can be saved to the flash memory using specific indices. Valid indices for storing certificates are 1 and 2, as 0 is reserved for the default certificate. +* RAM Memory Loading: +Certificates can be loaded from RAM, where valid indices range from 0 to 2. The device publishes a hash of the certificate to the broker to verify its identity. If the connection using the current certificate fails, the device will revert to the previous certificate. + +### Use Case: Changing a Group of Certificates +When updating certificates, follow these steps to ensure that the new certificates are correctly loaded and used: + +1. Push Certificates via MQTT: +Send the new certificates one by one through MQTT, using indices 1 or 2. Replace newline characters (\n) in the certificates with spaces. +```json +{ + "cnt_index": 1, + "mqtt_server_cert": "-----BEGIN CERTIFICATE----- MIIDQTCC----END CERTIFICATE-----" +} +``` + +Accepted certificates are: +* `mqtt_server_cert` +* `mqtt_client_cert` +* `mqtt_client_key` +* `ota_server_cert` + +2. Verify Certificates in RAM: +After pushing the certificates, verify that they have been correctly loaded into RAM. +```json +{ + "cnt_index": 1, + "read_cnt": true +} +``` + +3. Test and Save Certificates: +Once verification is complete, test the connection using the new certificates. If the connection is successful, send the command to save the certificates to flash. +```json +{ + "cnt_index": 1, + "save_cnt": true +} +``` + +4. Broker Connection: +The broker will attempt to use the newly received certificates for the connection. + +5. Successful Connection Handling: +If the connection is successful, the certificates are permanently stored in the flash memory at the specified index. + +6. Handling Connection Failures: +If the connection fails, the device will revert to the previously used certificate index, and the new certificates will not be saved. + # Firmware update from MQTT (ESP only) When the gateway used is from a standard ESP32 environment [listed and defined here](https://github.com/1technophile/OpenMQTTGateway/blob/development/environments.ini), it can be updated through a simple MQTT command: @@ -144,7 +220,7 @@ OpenMQTTGateway checks at start and every hour if an update is available. Alternatively if you want to choose the update URL you can use the command below (ESP32 and ESP8266): -Without certificate, in this case we will use the root certificate defined in User_config.h +Without certificate, in this case the gateway will use the ota_server_cert certificate defined in default_ota_cert.h ``` mosquitto_pub -t "home/OpenMQTTGateway_ESP32_BLE/commands/MQTTtoSYS/firmware_update" -m '{ "version": "test", @@ -153,39 +229,29 @@ mosquitto_pub -t "home/OpenMQTTGateway_ESP32_BLE/commands/MQTTtoSYS/firmware_upd }' ``` -With certificate: +With certificate (replace the \n in the certificate by spaces to publish it easily): ``` mosquitto_pub -t "home/OpenMQTTGateway_ESP32_BLE/commands/MQTTtoSYS/firmware_update" -m '{ "version": "test", "password": "OTAPASSWORD", "url": "https://github.com/1technophile/OpenMQTTGateway/releases/download/v0.9.12/esp32dev-ble-firmware.bin", - "server_cert": "-----BEGIN CERTIFICATE----- -MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD -QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB -CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 -nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt -43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P -T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 -gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO -BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR -TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw -DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr -hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg -06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF -PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls -YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk -CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= ------END CERTIFICATE-----"}' + "ota_server_cert": "-----BEGIN CERTIFICATE----- MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= -----END CERTIFICATE-----"}' ``` -A bash script is available [here](ota_command_cert.zip) to simplify the use of the `server_cert` parameter. +A bash script is available [here also](ota_command_cert.zip) to simplify the use of the `server_cert` parameter. +Alternatively the OTA certificate can also be saved with the cnt_index for future use: + +``` +mosquitto_pub -t "home/OpenMQTTGateway_ESP32_BLE/commands/MQTTtoSYS/config" -m '{ + "cnt_index": 1, + "save_cnt":true, + "ota_server_cert": "-----BEGIN CERTIFICATE----- MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= -----END CERTIFICATE-----" +}' +``` +The other connection parameters corresponding to the index need to be valid for the save function to work. This command will switch to connection parameters of index 1. + To enable this functionality, `MQTT_HTTPS_FW_UPDATE` will need to be defined or the line that defines in in user_config.h will need to be uncommented. ::: tip diff --git a/environments.ini b/environments.ini index 5e25ac8f..5b44a159 100644 --- a/environments.ini +++ b/environments.ini @@ -328,9 +328,10 @@ build_flags = '-DZgatewayBT="BT"' '-DLED_SEND_RECEIVE=2' '-DLED_SEND_RECEIVE_ON=0' + '-DARDUINO_LOOP_STACK_SIZE=15000' '-DMQTT_SECURE_DEFAULT=true' - '-DMQTT_SECURE_SELF_SIGNED' '-DMQTT_CERT_VALIDATE_DEFAULT=true' + '-DMQTT_SECURE_SIGNED_CLIENT=true' '-DMQTT_SERVER="xxxxxxxxxxxxx-ats.iot.eu-west-2.amazonaws.com"' '-DMQTT_PORT="8883"' '-DMQTT_USER=""' @@ -1366,18 +1367,6 @@ build_flags = '-DGateway_Name="OMG_TEST_MANUAL_WIFI"' board_build.flash_mode = dout -[env:nodemcuv2-mqtt-fw-test] -platform = ${com.esp8266_platform} -board = nodemcuv2 -lib_deps = - ${com-esp.lib_deps} - ${libraries.wifimanager8266} -build_flags = - ${com-esp.build_flags} - '-DMQTT_SECURE_SELF_SIGNED' - '-DGateway_Name="OMG_TEST_MQTT_FW"' -board_build.flash_mode = dout - [env:rf-wifi-gateway] platform = ${com.esp8266_platform} board = nodemcuv2 @@ -1748,4 +1737,5 @@ build_flags = '-DLED_SEND_RECEIVE_ON=0' '-DGateway_Name="OMG_ESP32_BLE"' '-DUSE_BLUFI=1' + '-DARDUINO_LOOP_STACK_SIZE=15000' custom_description = Regular BLE gateway based on esp-idf with adaptive scanning activated, automatically adapts the scan parameters depending on your devices diff --git a/main/User_config.h b/main/User_config.h index f6e3547e..bed07554 100644 --- a/main/User_config.h +++ b/main/User_config.h @@ -147,17 +147,30 @@ const byte mac[] = {0xDE, 0xED, 0xBA, 0xFE, 0x54, 0x95}; //W5100 ethernet shield //MQTT Parameters definition #define parameters_size 65 #define mqtt_topic_max_size 150 -#ifndef mqtt_max_packet_size -# ifdef MQTT_HTTPS_FW_UPDATE -# ifndef CHECK_OTA_UPDATE -# define CHECK_OTA_UPDATE true // enable to check for the presence of a new version for your environment on Github -# endif -# define mqtt_max_packet_size 2560 -# else -# define mqtt_max_packet_size 1024 +#define mqtt_key_max_size 20 +#ifdef MQTT_HTTPS_FW_UPDATE +# ifndef CHECK_OTA_UPDATE +# define CHECK_OTA_UPDATE true // enable to check for the presence of a new version for your environment on Github # endif #endif +#ifndef JSON_MSG_BUFFER +# if defined(ESP32) +# define JSON_MSG_BUFFER 816 // adjusted to minimum size covering largest Theengs device JSON properties (RuuviTag_RAWv2) +# elif defined(ESP8266) +# define JSON_MSG_BUFFER 512 // Json message max buffer size, don't put 768 or higher it is causing unexpected behaviour on ESP8266, certificates handling with ESP8266 is not tested +# endif +# if MQTT_SECURE_DEFAULT +# define JSON_MSG_BUFFER_MAX 2048 // Json message buffer size increased to handle certificate changes through MQTT, used for the queue and the coming MQTT messages +# else +# define JSON_MSG_BUFFER_MAX JSON_MSG_BUFFER +# endif +#endif + +#ifndef mqtt_max_topic_size +# define mqtt_max_packet_size JSON_MSG_BUFFER_MAX + mqtt_topic_max_size + 10 // maximum size of the MQTT packet +#endif + #ifndef MQTT_USER # define MQTT_USER "your_username" #endif @@ -181,103 +194,103 @@ const byte mac[] = {0xDE, 0xED, 0xBA, 0xFE, 0x54, 0x95}; //W5100 ethernet shield # define QueueSemaphoreTimeOutLoop 100 // time out for semaphore retrieval from the loop #endif -#if defined(ESP8266) || defined(ESP32) // Uncomment to use a device running TheengsGateway to decode BLE data. (https://github.com/theengs/gateway) // Set the topic to the subscribe topic configured in the TheengGateway // #define MQTTDecodeTopic "MQTTDecode" -// The root ca certificate used for validating the MQTT broker -// The certificate must be in PEM ascii format -const char* certificate PROGMEM = R"EOF(" ------BEGIN CERTIFICATE----- -... ------END CERTIFICATE----- -")EOF"; +#define ATTEMPTS_BEFORE_BG 10 // Number of wifi connection attempts before going to BG protocol +#define ATTEMPTS_BEFORE_B 20 // Number of wifi connection attempts before going to B protocol -# define ATTEMPTS_BEFORE_BG 10 // Number of wifi connection attempts before going to BG protocol -# define ATTEMPTS_BEFORE_B 20 // Number of wifi connection attempts before going to B protocol +#ifndef NTP_SERVER +# define NTP_SERVER "pool.ntp.org" +#endif -# ifndef NTP_SERVER -# define NTP_SERVER "pool.ntp.org" -# endif +#ifndef MQTT_SECURE_DEFAULT +# define MQTT_SECURE_DEFAULT false +#endif -# ifndef MQTT_SECURE_DEFAULT -# define MQTT_SECURE_DEFAULT false -# endif +#ifndef MQTT_CERT_VALIDATE_DEFAULT +# define MQTT_CERT_VALIDATE_DEFAULT false +#endif -# ifndef MQTT_CERT_VALIDATE_DEFAULT -# define MQTT_CERT_VALIDATE_DEFAULT false -# endif +#ifndef AWS_IOT +# define AWS_IOT false +#endif -# ifndef AWS_IOT -# define AWS_IOT false -# endif - -# if AWS_IOT +#if AWS_IOT // Enable the use of ALPN for AWS IoT Core with the port 443 const char* alpnProtocols[] = {"x-amzn-mqtt-ca", NULL}; -# endif +#endif //# define MQTT_HTTPS_FW_UPDATE //uncomment to enable updating via MQTT message. -# ifdef MQTT_HTTPS_FW_UPDATE +#ifdef MQTT_HTTPS_FW_UPDATE // If used, this should be set to the root CA certificate of the server hosting the firmware. -# ifdef PRIVATE_CERTS -# include "certs/private_ota_cert.h" -# else -# include "certs/default_ota_cert.h" -# endif - -# ifndef MQTT_HTTPS_FW_UPDATE_USE_PASSWORD -# define MQTT_HTTPS_FW_UPDATE_USE_PASSWORD 1 // Set this to 0 if not using TLS connection to MQTT broker to prevent clear text passwords being sent. -# endif -# if DEVELOPMENTOTA -# define OTA_JSON_URL "https://github.com/1technophile/OpenMQTTGateway/raw/gh-pages/dev/firmware_build/latest_version_dev.json" //OTA url used to discover new versions of the firmware from development nightly builds -# else -# define OTA_JSON_URL "https://github.com/1technophile/OpenMQTTGateway/raw/gh-pages/firmware_build/latest_version.json" //OTA url used to discover new versions of the firmware -# endif -# define ENTITY_PICTURE "https://github.com/1technophile/OpenMQTTGateway/raw/development/docs/img/Openmqttgateway_logo_mini_margins.png" -# define RELEASE_LINK_DEV "https://github.com/1technophile/OpenMQTTGateway/raw/gh-pages/dev/firmware_build/" -# define RELEASE_LINK "https://github.com/1technophile/OpenMQTTGateway/releases/download/" +# ifdef PRIVATE_CERTS +# include "certs/private_ota_cert.h" +# else +# include "certs/default_ota_cert.h" # endif -# ifndef MQTT_SECURE_SELF_SIGNED -# define MQTT_SECURE_SELF_SIGNED 0 +# ifndef MQTT_HTTPS_FW_UPDATE_USE_PASSWORD +# define MQTT_HTTPS_FW_UPDATE_USE_PASSWORD 1 // Set this to 0 if not using TLS connection to MQTT broker to prevent clear text passwords being sent. # endif - -# ifndef MQTT_SECURE_SELF_SIGNED_CLIENT -# define MQTT_SECURE_SELF_SIGNED_CLIENT 1 // If using a self signed certificate for the broker and not using client certificates set this to false or 0 +# if DEVELOPMENTOTA +# define OTA_JSON_URL "https://github.com/1technophile/OpenMQTTGateway/raw/gh-pages/dev/firmware_build/latest_version_dev.json" //OTA url used to discover new versions of the firmware from development nightly builds +# else +# define OTA_JSON_URL "https://github.com/1technophile/OpenMQTTGateway/raw/gh-pages/firmware_build/latest_version.json" //OTA url used to discover new versions of the firmware # endif +# define ENTITY_PICTURE "https://github.com/1technophile/OpenMQTTGateway/raw/development/docs/img/Openmqttgateway_logo_mini_margins.png" +# define RELEASE_LINK_DEV "https://github.com/1technophile/OpenMQTTGateway/raw/gh-pages/dev/firmware_build/" +# define RELEASE_LINK "https://github.com/1technophile/OpenMQTTGateway/releases/download/" +#else +const char* OTAserver_cert = ""; +#endif -# ifndef MQTT_SECURE_SELF_SIGNED_INDEX_DEFAULT -# define MQTT_SECURE_SELF_SIGNED_INDEX_DEFAULT 0 -# endif +#ifndef MQTT_SECURE_SIGNED_CLIENT +# define MQTT_SECURE_SIGNED_CLIENT 0 // If using a signed certificate for the broker and using client certificate/key set this to true or 1 +#endif -# if MQTT_SECURE_SELF_SIGNED -# ifdef PRIVATE_CERTS -# include "certs/private_client_cert.h" -# include "certs/private_client_key.h" -# include "certs/private_server_cert.h" -# else -# include "certs/default_client_cert.h" -# include "certs/default_client_key.h" -# include "certs/default_server_cert.h" -# endif +#ifndef CNT_DEFAULT_INDEX +# define CNT_DEFAULT_INDEX 0 // Default set of connection parameters +#endif -struct ss_certs { - const char* server_cert; - const char* client_cert; - const char* client_key; +#ifdef PRIVATE_CERTS +# include "certs/private_client_cert.h" +# include "certs/private_client_key.h" +# include "certs/private_server_cert.h" +#else +# include "certs/default_client_cert.h" +# include "certs/default_client_key.h" +# include "certs/default_server_cert.h" +#endif + +#include + +struct ss_cnt_parameters { + std::string server_cert; + std::string client_cert; + std::string client_key; + std::string ota_server_cert; + char mqtt_server[parameters_size]; + char mqtt_port[6]; + char mqtt_user[parameters_size]; + char mqtt_pass[parameters_size]; + bool isConnectionSecure; + bool isCertValidate; + bool validConnection; }; -struct ss_certs certs_array[2] = { - {ss_server_cert, ss_client_cert, ss_client_key}, - {ss_server_cert, ss_client_cert, ss_client_key}}; +// Index 0 is used for connection parameters provided in the build that can be overloaded by WiFi Manager/Onboarding/WebUI,MQTT +#define CNT_DEFAULT_INDEX 0 +// Index 1 and more are used for connection parameters provided at runtime by MQTT -static_assert(MQTT_SECURE_SELF_SIGNED_INDEX_DEFAULT < (sizeof(certs_array) / sizeof(ss_certs)), - "Invalid MQTT self signed default index"); -# endif -#endif +ss_cnt_parameters cnt_parameters_array[3] = { + {ss_server_cert, ss_client_cert, ss_client_key, OTAserver_cert, MQTT_SERVER, MQTT_PORT, MQTT_USER, MQTT_PASS, MQTT_SECURE_DEFAULT, MQTT_CERT_VALIDATE_DEFAULT, true}, + {"", "", "", "", MQTT_SERVER, MQTT_PORT, MQTT_USER, MQTT_PASS, MQTT_SECURE_DEFAULT, MQTT_CERT_VALIDATE_DEFAULT, false}, + {"", "", "", "", MQTT_SERVER, MQTT_PORT, MQTT_USER, MQTT_PASS, MQTT_SECURE_DEFAULT, MQTT_CERT_VALIDATE_DEFAULT, false}}; + +#define MIN_CERT_LENGTH 200 // Minimum length of a certificate to be considered valid /** * Deep-sleep for the ESP8266. @@ -655,16 +668,6 @@ Adafruit_NeoPixel leds2(ANEOPIX_IND_NUM_LEDS, ANEOPIX_IND_DATA_GPIO2, ANEOPIX_IN # define valueAsATopic false // define true to integrate msg value into the subject when receiving #endif -#if defined(ESP32) -# define JSON_MSG_BUFFER 816 // adjusted to minimum size covering largest Theengs device JSON properties (RuuviTag_RAWv2) -# define SIGNAL_SIZE_UL_ULL uint64_t -# define STRTO_UL_ULL strtoull -#elif defined(ESP8266) -# define JSON_MSG_BUFFER 512 // Json message max buffer size, don't put 768 or higher it is causing unexpected behaviour on ESP8266 -# define SIGNAL_SIZE_UL_ULL uint64_t -# define STRTO_UL_ULL strtoull -#endif - #if defined(ZgatewayRF) || defined(ZgatewayIR) || defined(ZgatewaySRFB) || defined(ZgatewayWeatherStation) || defined(ZgatewayRTL_433) // variable to avoid duplicates # ifndef time_avoid_duplicate @@ -715,9 +718,6 @@ char mqtt_topic[parameters_size + 1] = Base_Topic; char gateway_name[parameters_size + 1] = Gateway_Name; void connectMQTT(); -#ifndef ESPWifiManualSetup -void saveConfig(); -#endif unsigned long uptime(); bool cmpToMainTopic(const char*, const char*); @@ -731,40 +731,43 @@ void pub(const char*, const char*); Preferences preferences; #endif -#ifdef ZmqttDiscovery unsigned long lastDiscovery = 0; // Time of the last discovery to trigger automaticaly to off after DiscoveryAutoOffTimer +#ifndef DEFAULT_DISCOVERY +# define DEFAULT_DISCOVERY true #endif -#if defined(ESP8266) || defined(ESP32) -# include +#include // Flags definition for white list, black list, discovery management -# define device_flags_init 0 << 0 -# define device_flags_isDisc 1 << 0 -# define device_flags_isWhiteL 1 << 1 -# define device_flags_isBlackL 1 << 2 -# define device_flags_connect 1 << 3 -# define isWhite(device) device->isWhtL -# define isBlack(device) device->isBlkL -# define isDiscovered(device) device->isDisc +#define device_flags_init 0 << 0 +#define device_flags_isDisc 1 << 0 +#define device_flags_isWhiteL 1 << 1 +#define device_flags_isBlackL 1 << 2 +#define device_flags_connect 1 << 3 +#define isWhite(device) device->isWhtL +#define isBlack(device) device->isBlkL +#define isDiscovered(device) device->isDisc /*--------------------Minimum freeHeap--------------------*/ // Below this parameter we trigger a restart, this avoid stuck boards like seen in https://github.com/1technophile/OpenMQTTGateway/issues/1693 -# define MinimumMemory 40000 +#define MinimumMemory 40000 /*----------------CONFIGURABLE PARAMETERS-----------------*/ struct SYSConfig_s { + bool XtoMQTT; // if true the gateway will publish the received data on the MQTT broker bool discovery; // HA discovery convention bool ohdiscovery; // OH discovery specificities -# ifdef RGB_INDICATORS +#ifdef RGB_INDICATORS int rgbbrightness; // brightness of the RGB LED -# endif +#endif }; +#ifndef DEFAULT_XtoMQTT +# define DEFAULT_XtoMQTT true #endif #if defined(ZgatewayRF) || defined(ZgatewayIR) || defined(ZgatewaySRFB) || defined(ZgatewayWeatherStation) || defined(ZgatewayRTL_433) -bool isAduplicateSignal(SIGNAL_SIZE_UL_ULL); -void storeSignalValue(SIGNAL_SIZE_UL_ULL); +bool isAduplicateSignal(uint64_t); +void storeSignalValue(uint64_t); #endif // Origin topics diff --git a/main/Zblufi.ino b/main/Zblufi.ino index ffa9f56d..4980d4a0 100644 --- a/main/Zblufi.ino +++ b/main/Zblufi.ino @@ -57,38 +57,45 @@ static void example_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_para * now, as a example, we do it more simply */ switch (event) { case ESP_BLUFI_EVENT_INIT_FINISH: - Log.notice(F("BLUFI init finish" CR)); + Log.trace(F("BLUFI init finish" CR)); esp_blufi_adv_start(); break; case ESP_BLUFI_EVENT_DEINIT_FINISH: - Log.notice(F("BLUFI deinit finish" CR)); + Log.trace(F("BLUFI deinit finish" CR)); NimBLEDevice::deinit(true); break; case ESP_BLUFI_EVENT_BLE_CONNECT: + Log.trace(F("BLUFI BLE connect" CR)); omg_blufi_ble_connected = true; esp_blufi_adv_stop(); blufi_security_init(); break; case ESP_BLUFI_EVENT_BLE_DISCONNECT: + Log.trace(F("BLUFI BLE disconnect" CR)); omg_blufi_ble_connected = false; blufi_security_deinit(); if (WiFi.isConnected()) { esp_blufi_deinit(); +# ifndef ESPWifiManualSetup + wifiManager.stopConfigPortal(); +# endif } else { esp_blufi_adv_start(); } break; case ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP: - Log.notice(F("BLUFI requset wifi connect to AP" CR)); + Log.trace(F("BLUFI request wifi connect to AP" CR)); WiFi.begin((char*)gl_sta_ssid, (char*)gl_sta_passwd); gl_sta_is_connecting = true; + blufiConnectAP = true; break; case ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP: - Log.notice(F("BLUFI requset wifi disconnect from AP\n" CR)); + Log.trace(F("BLUFI request wifi disconnect from AP\n" CR)); WiFi.disconnect(); + blufiConnectAP = false; break; case ESP_BLUFI_EVENT_REPORT_ERROR: - Log.notice(F("BLUFI report error, error code %d\n" CR), param->report_error.state); + Log.trace(F("BLUFI report error, error code %d\n" CR), param->report_error.state); esp_blufi_send_error_info(param->report_error.state); break; case ESP_BLUFI_EVENT_GET_WIFI_STATUS: { @@ -109,6 +116,7 @@ static void example_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_para break; } case ESP_BLUFI_EVENT_RECV_SLAVE_DISCONNECT_BLE: + Log.trace(F("BLUFI recv slave disconnect a ble connection" CR)); esp_blufi_disconnect(); break; case ESP_BLUFI_EVENT_RECV_STA_SSID: @@ -137,20 +145,40 @@ static void example_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_para } if (!json.isNull()) { Log.trace(F("\nparsed json, size: %u" CR), json.memoryUsage()); - if (json.containsKey("mqtt_server")) - strcpy(mqtt_server, json["mqtt_server"]); - if (json.containsKey("mqtt_port")) - strcpy(mqtt_port, json["mqtt_port"]); - if (json.containsKey("mqtt_user")) - strcpy(mqtt_user, json["mqtt_user"]); - if (json.containsKey("mqtt_pass")) - strcpy(mqtt_pass, json["mqtt_pass"]); - if (json.containsKey("mqtt_topic")) - strcpy(mqtt_topic, json["mqtt_topic"]); - if (json.containsKey("mqtt_broker_secure")) - mqtt_secure = json["mqtt_broker_secure"].as(); + if (json.containsKey("mqtt_server") && json["mqtt_server"].is() && json["mqtt_server"].as().length() > 0 && json["mqtt_server"].as().length() < parameters_size) + strcpy(cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_server, json["mqtt_server"]); + if (json.containsKey("mqtt_port") && json["mqtt_port"].is() && json["mqtt_port"].as().length() > 0 && json["mqtt_port"].as().length() < parameters_size) + strcpy(cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_port, json["mqtt_port"]); + if (json.containsKey("mqtt_user") && json["mqtt_user"].is() && json["mqtt_user"].as().length() > 0 && json["mqtt_user"].as().length() < parameters_size) + strcpy(cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_user, json["mqtt_user"]); + if (json.containsKey("mqtt_pass") && json["mqtt_pass"].is() && json["mqtt_pass"].as().length() > 0 && json["mqtt_pass"].as().length() < parameters_size) + strcpy(cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_pass, json["mqtt_pass"]); + + if (json.containsKey("mqtt_broker_secure") && json["mqtt_broker_secure"].is()) + cnt_parameters_array[CNT_DEFAULT_INDEX].isConnectionSecure = json["mqtt_broker_secure"].as(); + if (json.containsKey("mqtt_iscertvalid") && json["mqtt_iscertvalid"].is()) + cnt_parameters_array[CNT_DEFAULT_INDEX].isCertValidate = json["mqtt_iscertvalid"].as(); + if (json.containsKey("mqtt_broker_cert") && json["mqtt_broker_cert"].is() && json["mqtt_broker_cert"].as().length() > MIN_CERT_LENGTH) + cnt_parameters_array[CNT_DEFAULT_INDEX].server_cert = json["mqtt_broker_cert"].as(); + if (json.containsKey("mqtt_client_cert") && json["mqtt_client_cert"].is() && json["mqtt_client_cert"].as().length() > MIN_CERT_LENGTH) + cnt_parameters_array[CNT_DEFAULT_INDEX].client_cert = json["mqtt_client_cert"].as(); + if (json.containsKey("mqtt_client_key") && json["mqtt_client_key"].is() && json["mqtt_client_key"].as().length() > MIN_CERT_LENGTH) + cnt_parameters_array[CNT_DEFAULT_INDEX].client_key = json["mqtt_client_key"].as(); + if (json.containsKey("ota_server_cert") && json["ota_server_cert"].is() && json["ota_server_cert"].as().length() > MIN_CERT_LENGTH) + cnt_parameters_array[CNT_DEFAULT_INDEX].ota_server_cert = json["ota_server_cert"].as(); if (json.containsKey("gateway_name")) strcpy(gateway_name, json["gateway_name"]); + if (json.containsKey("mqtt_topic")) + strcpy(mqtt_topic, json["mqtt_topic"]); + if (json.containsKey("ota_pass")) + strcpy(ota_pass, json["ota_pass"]); + + if (json.containsKey("cnt_index") && json["cnt_index"].is() && json["cnt_index"].as() > 0 && json["cnt_index"].as() < 3) { + cnt_index = json["cnt_index"].as(); + } else { + cnt_index = CNT_DEFAULT_INDEX; + } + saveConfig(); } break; diff --git a/main/ZgatewayBT.ino b/main/ZgatewayBT.ino index e14d92e5..bafd1309 100644 --- a/main/ZgatewayBT.ino +++ b/main/ZgatewayBT.ino @@ -800,7 +800,7 @@ void stopProcessing() { xSemaphoreGive(semaphoreBLEOperation); } } - Log.notice(F("BLE gateway stopped %T, free heap: %d" CR), ESP.getFreeHeap()); + Log.notice(F("BLE gateway stopped, free heap: %d" CR), ESP.getFreeHeap()); } void coreTask(void* pvParameters) { @@ -906,7 +906,7 @@ void setupBTTasksAndBLE() { procBLETask, /* Function to implement the task */ "procBLETask", /* Name of the task */ # ifdef USE_BLUFI - 13000, + 13500, # else 8500, /* Stack size in bytes */ # endif diff --git a/main/ZgatewayIR.ino b/main/ZgatewayIR.ino index 60796e9d..2454e027 100644 --- a/main/ZgatewayIR.ino +++ b/main/ZgatewayIR.ino @@ -114,7 +114,7 @@ void IRtoMQTT() { # ifdef ESP32 Log.trace(F("IR Task running on core :%d" CR), xPortGetCoreID()); # endif - IRdata["value"] = (SIGNAL_SIZE_UL_ULL)(results.value); + IRdata["value"] = uint64_t(results.value); IRdata["protocol"] = (int)(results.decode_type); IRdata["bits"] = (int)(results.bits); IRdata["hex"] = resultToHexidecimal(&results); @@ -141,7 +141,7 @@ void IRtoMQTT() { Log.trace(F("raw redirected" CR)); # endif irrecv.resume(); // Receive the next value - SIGNAL_SIZE_UL_ULL MQTTvalue = IRdata["value"].as(); + uint64_t MQTTvalue = IRdata["value"].as(); //trc(MQTTvalue); if ((pubIRunknownPrtcl == false && IRdata["protocol"].as() == -1)) { // don't publish unknown IR protocol Log.notice(F("--no pub unknwn prt--" CR)); @@ -159,7 +159,7 @@ void IRtoMQTT() { } } -bool sendIdentifiedProtocol(const char* protocol_name, SIGNAL_SIZE_UL_ULL data, const char* hex, unsigned int valueBITS, uint16_t valueRPT); +bool sendIdentifiedProtocol(const char* protocol_name, uint64_t data, const char* hex, unsigned int valueBITS, uint16_t valueRPT); # if jsonReceiving void MQTTtoIR(char* topicOri, JsonObject& IRdata) { @@ -254,7 +254,7 @@ void MQTTtoIR(char* topicOri, JsonObject& IRdata) { } # endif -bool sendIdentifiedProtocol(const char* protocol_name, SIGNAL_SIZE_UL_ULL data, const char* hex, unsigned int valueBITS, uint16_t valueRPT) { +bool sendIdentifiedProtocol(const char* protocol_name, uint64_t data, const char* hex, unsigned int valueBITS, uint16_t valueRPT) { uint8_t dataarray[valueBITS]; if (hex) { const char* ptr = NULL; diff --git a/main/ZgatewayRF.ino b/main/ZgatewayRF.ino index 6f2cbd13..e6394d2b 100644 --- a/main/ZgatewayRF.ino +++ b/main/ZgatewayRF.ino @@ -82,7 +82,7 @@ static char* dec2binWzerofill(unsigned long Dec, unsigned int bitLength) { # if defined(ZmqttDiscovery) && !defined(RF_DISABLE_TRANSMIT) && defined(RFmqttDiscovery) -void RFtoMQTTdiscovery(SIGNAL_SIZE_UL_ULL MQTTvalue) { +void RFtoMQTTdiscovery(uint64_t MQTTvalue) { //on the fly switch creation from received RF values char val[11]; sprintf(val, "%lu", MQTTvalue); @@ -114,11 +114,11 @@ void RFtoMQTT() { # ifdef ESP32 Log.trace(F("RF Task running on core :%d" CR), xPortGetCoreID()); # endif - SIGNAL_SIZE_UL_ULL MQTTvalue = mySwitch.getReceivedValue(); + uint64_t MQTTvalue = mySwitch.getReceivedValue(); int length = mySwitch.getReceivedBitlength(); const char* binary = dec2binWzerofill(MQTTvalue, length); - RFdata["value"] = (SIGNAL_SIZE_UL_ULL)MQTTvalue; + RFdata["value"] = (uint64_t)MQTTvalue; RFdata["protocol"] = (int)mySwitch.getReceivedProtocol(); RFdata["length"] = (int)mySwitch.getReceivedBitlength(); RFdata["delay"] = (int)mySwitch.getReceivedDelay(); @@ -168,7 +168,7 @@ void MQTTtoRF(char* topicOri, char* datacallback) { # endif mySwitch.disableReceive(); mySwitch.enableTransmit(RF_EMITTER_GPIO); - SIGNAL_SIZE_UL_ULL data = STRTO_UL_ULL(datacallback, NULL, 10); // we will not be able to pass values > 4294967295 on Arduino boards + uint64_t data = strtoull(datacallback, NULL, 10); // we will not be able to pass values > 4294967295 on Arduino boards // RF DATA ANALYSIS //We look into the subject to see if a special RF protocol is defined @@ -227,7 +227,7 @@ void MQTTtoRF(char* topicOri, char* datacallback) { void MQTTtoRF(char* topicOri, JsonObject& RFdata) { // json object decoding if (cmpToMainTopic(topicOri, subjectMQTTtoRF)) { Log.trace(F("MQTTtoRF json" CR)); - SIGNAL_SIZE_UL_ULL data = RFdata["value"]; + uint64_t data = RFdata["value"]; if (data != 0) { int valuePRT = RFdata["protocol"] | 1; int valuePLSL = RFdata["delay"] | 350; diff --git a/main/ZmqttDiscovery.ino b/main/ZmqttDiscovery.ino index 9bda9b36..0682f26c 100644 --- a/main/ZmqttDiscovery.ino +++ b/main/ZmqttDiscovery.ino @@ -1300,4 +1300,6 @@ void pubMqttDiscovery() { # endif # endif } +#else +void pubMqttDiscovery() {} #endif diff --git a/main/ZwebUI.ino b/main/ZwebUI.ino index 06d5dec6..06a9596e 100644 --- a/main/ZwebUI.ino +++ b/main/ZwebUI.ino @@ -708,34 +708,34 @@ void handleMQ() { if (server.hasArg("mh")) { WEBtoSYS["mqtt_server"] = server.arg("mh"); - if (strncmp(mqtt_server, server.arg("mh").c_str(), parameters_size)) { + if (strncmp(cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_server, server.arg("mh").c_str(), parameters_size)) { update = true; } } if (server.hasArg("ml")) { WEBtoSYS["mqtt_port"] = server.arg("ml"); - if (strncmp(mqtt_port, server.arg("ml").c_str(), 6)) { + if (strncmp(cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_port, server.arg("ml").c_str(), 6)) { update = true; } } if (server.hasArg("mu")) { WEBtoSYS["mqtt_user"] = server.arg("mu"); - if (strncmp(mqtt_user, server.arg("mu").c_str(), parameters_size)) { + if (strncmp(cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_user, server.arg("mu").c_str(), parameters_size)) { update = true; } } if (server.hasArg("mp")) { WEBtoSYS["mqtt_pass"] = server.arg("mp"); - if (strncmp(mqtt_pass, server.arg("mp").c_str(), parameters_size)) { + if (strncmp(cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_pass, server.arg("mp").c_str(), parameters_size)) { update = true; } } // SC - Secure Connection argument is only present when true - if (mqtt_secure != server.hasArg("sc")) { + if (cnt_parameters_array[CNT_DEFAULT_INDEX].isConnectionSecure != server.hasArg("sc")) { update = true; } WEBtoSYS["mqtt_secure"] = server.hasArg("sc"); @@ -764,7 +764,8 @@ void handleMQ() { # ifndef ESPWifiManualSetup if (update) { Log.warning(F("[WebUI] Save MQTT and Reconnect" CR)); - + WEBtoSYS["cnt_index"] = 1; // For the moment we only enable to change index 1 with WebUI + WEBtoSYS["save_cnt"] = true; char jsonChar[100]; serializeJson(modules, jsonChar, measureJson(modules) + 1); char buffer[WEB_TEMPLATE_BUFFER_MAX_SIZE]; @@ -801,7 +802,7 @@ void handleMQ() { response += String(script); response += String(style); // mqtt server (mh), mqtt port (ml), mqtt username (mu), mqtt password (mp), secure connection (sc), server certificate (msc), topic (mt) - snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, config_mqtt_body, jsonChar, gateway_name, mqtt_server, mqtt_port, mqtt_user, (mqtt_secure ? "checked" : ""), gateway_name, mqtt_topic); + snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, config_mqtt_body, jsonChar, gateway_name, cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_server, cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_port, cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_user, (cnt_parameters_array[CNT_DEFAULT_INDEX].isConnectionSecure ? "checked" : ""), gateway_name, mqtt_topic); response += String(buffer); snprintf(buffer, WEB_TEMPLATE_BUFFER_MAX_SIZE, footer, OMG_VERSION); response += String(buffer); diff --git a/main/main.ino b/main/main.ino index 2cda4efc..a768ec20 100644 --- a/main/main.ino +++ b/main/main.ino @@ -40,7 +40,7 @@ #if defined(ZgatewayRF) || defined(ZgatewayIR) || defined(ZgatewaySRFB) || defined(ZgatewayWeatherStation) || defined(ZgatewayRTL_433) // array to store previous received RFs, IRs codes and their timestamps struct ReceivedSignal { - SIGNAL_SIZE_UL_ULL value; + uint64_t value; uint32_t time; }; @@ -79,10 +79,8 @@ bool ready_to_sleep = false; #include #include -#include - struct JsonBundle { - StaticJsonDocument doc; + StaticJsonDocument doc; }; std::queue jsonQueue; @@ -90,6 +88,7 @@ std::queue jsonQueue; #ifdef ESP32 // Mutex to protect the queue SemaphoreHandle_t xQueueMutex; +bool blufiConnectAP = false; #endif StaticJsonDocument modulesBuffer; @@ -220,15 +219,11 @@ struct GfSun2000Data {}; #endif /*------------------------------------------------------------------------*/ -void setupTLS(bool self_signed = false, uint8_t index = 0); +void setupTLS(int index = CNT_DEFAULT_INDEX); //adding this to bypass the problem of the arduino builder issue 50 void callback(char* topic, byte* payload, unsigned int length); -char mqtt_user[parameters_size] = MQTT_USER; // not compulsory only if your broker needs authentication -char mqtt_pass[parameters_size] = MQTT_PASS; // not compulsory only if your broker needs authentication -char mqtt_server[parameters_size] = MQTT_SERVER; -char mqtt_port[6] = MQTT_PORT; char ota_pass[parameters_size] = gw_password; #ifdef USE_MAC_AS_GATEWAY_NAME # undef WifiManager_ssid @@ -249,11 +244,7 @@ static unsigned long last_ota_activity_millis = 0; SYSConfig_s SYSConfig; bool failSafeMode = false; -static bool mqtt_secure = MQTT_SECURE_DEFAULT; -static bool mqtt_cert_validate = MQTT_CERT_VALIDATE_DEFAULT; -static uint8_t mqtt_ss_index = MQTT_SECURE_SELF_SIGNED_INDEX_DEFAULT; -static String mqtt_cert = ""; -static String ota_server_cert = ""; +static int cnt_index = CNT_DEFAULT_INDEX; #ifdef ESP32 # include @@ -296,7 +287,7 @@ WiFiMulti wifiMulti; # include # include X509List caCert; -# if MQTT_SECURE_SELF_SIGNED_CLIENT +# if MQTT_SECURE_SIGNED_CLIENT X509List* pClCert = nullptr; PrivateKey* pClKey = nullptr; # endif @@ -425,7 +416,7 @@ void pubMainCore(JsonObject& data) { } // Add a document to the queue -void enqueueJsonObject(const StaticJsonDocument& jsonDoc) { +void enqueueJsonObject(const StaticJsonDocument& jsonDoc) { if (jsonDoc.size() == 0) { Log.error(F("Empty JSON, skipping" CR)); return; @@ -444,7 +435,7 @@ void enqueueJsonObject(const StaticJsonDocument& jsonDoc) { #ifdef ESP32 // Semaphore check before enqueueing a document -bool handleJsonEnqueue(const StaticJsonDocument& jsonDoc, int timeout) { +bool handleJsonEnqueue(const StaticJsonDocument& jsonDoc, int timeout) { if (xSemaphoreTake(xQueueMutex, pdMS_TO_TICKS(timeout))) { enqueueJsonObject(jsonDoc); xSemaphoreGive(xQueueMutex); @@ -572,7 +563,7 @@ void pub(const char* topicori, JsonObject& data) { topic = topic + "/" + protocol + "/" + value; } # else - SIGNAL_SIZE_UL_ULL value = data["value"]; + uint64_t value = data["value"]; if (value != 0) { topic = topic + "/" + toString(value); } @@ -592,11 +583,11 @@ void pub(const char* topicori, JsonObject& data) { # if defined(ESP8266) yield(); # endif - if (p.value().is() && strcmp(p.key().c_str(), "rssi") != 0) { //test rssi , bypass solution due to the fact that a int is considered as an SIGNAL_SIZE_UL_ULL + if (p.value().is() && strcmp(p.key().c_str(), "rssi") != 0) { //test rssi , bypass solution due to the fact that a int is considered as an uint64_t if (strcmp(p.key().c_str(), "value") == 0) { // if data is a value we don't integrate the name into the topic - pubMQTT(topic, p.value().as()); + pubMQTT(topic, p.value().as()); } else { // if data is not a value we integrate the name into the topic - pubMQTT(topic + "/" + String(p.key().c_str()), p.value().as()); + pubMQTT(topic + "/" + String(p.key().c_str()), p.value().as()); } } else if (p.value().is()) { pubMQTT(topic + "/" + String(p.key().c_str()), p.value().as()); @@ -651,12 +642,16 @@ void pubMQTT(const char* topic, const char* payload) { * @param retainFlag true if retain the retain Flag */ void pubMQTT(const char* topic, const char* payload, bool retainFlag) { - if (client.connected()) { - SendReceiveIndicatorON(); - Log.trace(F("[ OMG->MQTT ] topic: %s msg: %s " CR), topic, payload); - client.publish(topic, payload, retainFlag); + if (SYSConfig.XtoMQTT) { + if (client.connected()) { + SendReceiveIndicatorON(); + Log.trace(F("[ OMG->MQTT ] topic: %s msg: %s " CR), topic, payload); + client.publish(topic, payload, retainFlag); + } else { + Log.warning(F("Client not connected, aborting the publication" CR)); + } } else { - Log.warning(F("Client not connected, aborting the publication" CR)); + Log.trace(F("[ OMG->MQTT CANCELED] topic: %s msg: %s " CR), topic, payload); } } @@ -738,6 +733,21 @@ void pubMQTT(String topic, unsigned long payload) { pubMQTT(topic.c_str(), val); } +/* +* @brief Convert the spaces of the certificate into new lines +*/ +std::string processCert(const char* cert) { + std::string certStr(cert); + size_t pos = 0; + while ((pos = certStr.find(' ', pos)) != std::string::npos) { + if (pos < 4 || (pos >= 27 && pos <= certStr.length() - 25) || pos >= certStr.length() - 4) { + certStr.replace(pos, 1, "\n"); + } + pos++; + } + return certStr; +} + bool cmpToMainTopic(const char* topicOri, const char* toAdd) { // Is string "" equal to ""? // Compare first part with first chunk @@ -772,8 +782,9 @@ void delayWithOTA(long waitMillis) { } void SYSConfig_init() { + SYSConfig.XtoMQTT = DEFAULT_XtoMQTT; #ifdef ZmqttDiscovery - SYSConfig.discovery = true; + SYSConfig.discovery = DEFAULT_DISCOVERY; SYSConfig.ohdiscovery = OpenHABDiscovery; #endif #ifdef RGB_INDICATORS @@ -782,6 +793,7 @@ void SYSConfig_init() { } void SYSConfig_fromJson(JsonObject& SYSdata) { + Config_update(SYSdata, "xtomqtt", SYSConfig.XtoMQTT); #ifdef ZmqttDiscovery Config_update(SYSdata, "disc", SYSConfig.discovery); Config_update(SYSdata, "ohdisc", SYSConfig.ohdiscovery); @@ -835,9 +847,9 @@ void connectMQTT() { client.setBufferSize(mqtt_max_packet_size); client.setSocketTimeout(GeneralTimeOut - 1); #if AWS_IOT - if (client.connect(gateway_name, mqtt_user, mqtt_pass)) { // AWS doesn't support will topic for the moment + if (client.connect(gateway_name, cnt_parameters_array[cnt_index].mqtt_user, cnt_parameters_array[cnt_index].mqtt_pass)) { // AWS doesn't support will topic for the moment #else - if (client.connect(gateway_name, mqtt_user, mqtt_pass, topic, will_QoS, will_Retain, will_Message)) { + if (client.connect(gateway_name, cnt_parameters_array[cnt_index].mqtt_user, cnt_parameters_array[cnt_index].mqtt_pass, topic, will_QoS, will_Retain, will_Message)) { #endif displayPrint("MQTT connected"); @@ -864,10 +876,10 @@ void connectMQTT() { Log.warning(F("failure_number_mqtt: %d" CR), failure_number_mqtt); Log.warning(F("failed, rc=%d" CR), client.state()); #if defined(ESP32) - if (mqtt_secure) + if (cnt_parameters_array[cnt_index].isConnectionSecure) Log.warning(F("failed, ssl error code=%d" CR), ((WiFiClientSecure*)eClient)->lastError(nullptr, 0)); #elif defined(ESP8266) - if (mqtt_secure) + if (cnt_parameters_array[cnt_index].isConnectionSecure) Log.warning(F("failed, ssl error code=%d" CR), ((WiFiClientSecure*)eClient)->getLastSSLError()); #endif ErrorIndicatorON(); @@ -1111,6 +1123,14 @@ void setup() { #endif Log.trace(F("OpenMQTTGateway mac: %s" CR), WiFi.macAddress().c_str()); Log.trace(F("OpenMQTTGateway ip: %s" CR), WiFi.localIP().toString().c_str()); + Log.trace(F("OpenMQTTGateway index %d" CR), cnt_index); + Log.trace(F("OpenMQTTGateway mqtt server: %s" CR), cnt_parameters_array[cnt_index].mqtt_server); + Log.trace(F("OpenMQTTGateway mqtt port: %s" CR), cnt_parameters_array[cnt_index].mqtt_port); + Log.trace(F("OpenMQTTGateway mqtt user: %s" CR), cnt_parameters_array[cnt_index].mqtt_user); + Log.trace(F("OpenMQTTGateway mqtt topic: %s" CR), mqtt_topic); + Log.trace(F("OpenMQTTGateway gateway name: %s" CR), gateway_name); + Log.trace(F("OpenMQTTGateway secure connection: %s" CR), cnt_parameters_array[cnt_index].isConnectionSecure ? "true" : "false"); + Log.trace(F("OpenMQTTGateway validate cert: %s" CR), cnt_parameters_array[cnt_index].isCertValidate ? "true" : "false"); setOTA(); @@ -1119,14 +1139,9 @@ void setup() { modules.add(ZwebUI); #endif - if (mqtt_secure) { + if (cnt_parameters_array[cnt_index].isConnectionSecure) { eClient = new WiFiClientSecure; - if (mqtt_cert_validate) { - setupTLS(MQTT_SECURE_SELF_SIGNED, mqtt_ss_index); - } else { - WiFiClientSecure* sClient = (WiFiClientSecure*)eClient; - sClient->setInsecure(); - } + setupTLS(cnt_index); } else { eClient = new WiFiClient; } @@ -1136,10 +1151,10 @@ void setup() { Log.trace(F("Connecting to MQTT by mDNS without MQTT hostname" CR)); connectMQTTmdns(); #else - uint16_t port = strtol(mqtt_port, NULL, 10); + uint16_t port = strtol(cnt_parameters_array[cnt_index].mqtt_port, NULL, 10); Log.trace(F("Port: %l" CR), port); - Log.trace(F("Mqtt server: %s" CR), mqtt_server); - client.setServer(mqtt_server, port); + Log.trace(F("Mqtt server: %s" CR), cnt_parameters_array[cnt_index].mqtt_server); + client.setServer(cnt_parameters_array[cnt_index].mqtt_server, port); #endif client.setCallback(callback); @@ -1398,55 +1413,67 @@ void setOTA() { ArduinoOTA.begin(); } -void setupTLS(bool self_signed, uint8_t index) { +void setupTLS(int index) { configTime(0, 0, NTP_SERVER); WiFiClientSecure* sClient = (WiFiClientSecure*)eClient; -#if MQTT_SECURE_SELF_SIGNED - if (self_signed) { - Log.notice(F("Using self signed cert index %u" CR), index); -# if defined(ESP32) - sClient->setCACert(certs_array[index].server_cert); -# if AWS_IOT - if (strcmp(mqtt_port, "443") == 0) { + Log.notice(F("cnt index used: %d" CR), index); + if (!cnt_parameters_array[index].isCertValidate) { + Log.notice(F("Disabling cert validation" CR)); + sClient->setInsecure(); + } else { + Log.notice(F("Enabling cert validation" CR)); +#if defined(ESP32) + if (cnt_parameters_array[index].server_cert.length() > MIN_CERT_LENGTH) { + sClient->setCACert(cnt_parameters_array[index].server_cert.c_str()); + Log.notice(F("Server cert found from cert array" CR)); + } else if (strlen(ss_server_cert) > MIN_CERT_LENGTH) { + sClient->setCACert(ss_server_cert); + Log.notice(F("Server cert found from ss_server_cert" CR)); + } else { + Log.error(F("No server cert found" CR)); + } + +# if AWS_IOT + if (strcmp(cnt_parameters_array[index].mqtt_port, "443") == 0) { + Log.notice(F("Using ALPN" CR)); sClient->setAlpnProtocols(alpnProtocols); } -# endif -# if MQTT_SECURE_SELF_SIGNED_CLIENT - sClient->setCertificate(certs_array[index].client_cert); - sClient->setPrivateKey(certs_array[index].client_key); -# endif -# elif defined(ESP8266) - caCert.append(certs_array[index].server_cert); +# endif +# if MQTT_SECURE_SIGNED_CLIENT + if (cnt_parameters_array[index].client_cert.length() > MIN_CERT_LENGTH) { + sClient->setCertificate(cnt_parameters_array[index].client_cert.c_str()); + Log.notice(F("Client cert found from cert array" CR)); + } else if (strlen(ss_client_cert) > MIN_CERT_LENGTH) { + sClient->setCertificate(ss_client_cert); + Log.notice(F("Client cert found from ss_client_cert" CR)); + } else { + Log.error(F("No client cert found" CR)); + } + if (cnt_parameters_array[index].client_key.length() > MIN_CERT_LENGTH) { + sClient->setPrivateKey(cnt_parameters_array[index].client_key.c_str()); + Log.notice(F("Client key found from cert array" CR)); + } else if (strlen(ss_client_key) > MIN_CERT_LENGTH) { + sClient->setPrivateKey(ss_client_key); + Log.notice(F("Client key found from ss_client_key" CR)); + } else { + Log.error(F("No client key found" CR)); + } +# endif +#elif defined(ESP8266) + caCert.append(cnt_parameters_array[index].server_cert.c_str()); sClient->setTrustAnchors(&caCert); sClient->setBufferSizes(512, 512); -# if MQTT_SECURE_SELF_SIGNED_CLIENT +# if MQTT_SECURE_SIGNED_CLIENT if (pClCert != nullptr) { delete pClCert; } if (pClKey != nullptr) { delete pClKey; } - pClCert = new X509List(certs_array[index].client_cert); - pClKey = new PrivateKey(certs_array[index].client_key); + pClCert = new X509List(cnt_parameters_array[index].client_cert.c_str()); + pClKey = new PrivateKey(cnt_parameters_array[index].client_key.c_str()); sClient->setClientRSACert(pClCert, pClKey); -# endif # endif - } else -#endif - { - if (mqtt_cert.length() > 0) { -#if defined(ESP32) - sClient->setCACert(mqtt_cert.c_str()); - } else { - sClient->setCACert(certificate); - } -#elif defined(ESP8266) - caCert.append(mqtt_cert.c_str()); - } else { - caCert.append(certificate); - } - sClient->setTrustAnchors(&caCert); - sClient->setBufferSizes(512, 512); #endif } } @@ -1611,18 +1638,81 @@ void checkButton() {} # endif void saveConfig() { - Log.trace(F("saving config" CR)); - DynamicJsonDocument json(512 + ota_server_cert.length() + mqtt_cert.length()); - json["mqtt_server"] = mqtt_server; - json["mqtt_port"] = mqtt_port; - json["mqtt_user"] = mqtt_user; - json["mqtt_pass"] = mqtt_pass; + Log.trace(F("saving configs" CR)); + int totalSize = 512; + + for (int i = 0; i < 3; ++i) { // index 0 contains the default values from the build, these values can't be changed at runtime + if (cnt_parameters_array[i].validConnection) { + totalSize += cnt_parameters_array[i].server_cert.length(); + totalSize += cnt_parameters_array[i].client_cert.length(); + totalSize += cnt_parameters_array[i].client_key.length(); + totalSize += cnt_parameters_array[i].ota_server_cert.length(); + } + } + + Log.notice(F("Total size: %d" CR), totalSize); + + DynamicJsonDocument json(512 + totalSize); + for (int i = 0; i < 3; ++i) { + if (cnt_parameters_array[i].validConnection) { + char index_suffix[2]; + if (i == 0) { + index_suffix[0] = '\0'; + } else { + index_suffix[0] = '0' + i; + index_suffix[1] = '\0'; + } + char key[mqtt_key_max_size]; + if (cnt_parameters_array[i].server_cert.length() > MIN_CERT_LENGTH) { + strcpy(key, "mqtt_broker_cert"); + strcat(key, index_suffix); + json[key] = cnt_parameters_array[i].server_cert; + } + if (cnt_parameters_array[i].client_cert.length() > MIN_CERT_LENGTH) { + strcpy(key, "mqtt_client_cert"); + strcat(key, index_suffix); + json[key] = cnt_parameters_array[i].client_cert; + } + if (cnt_parameters_array[i].client_key.length() > MIN_CERT_LENGTH) { + strcpy(key, "mqtt_client_key"); + strcat(key, index_suffix); + json[key] = cnt_parameters_array[i].client_key; + } + if (cnt_parameters_array[i].ota_server_cert.length() > MIN_CERT_LENGTH) { + strcpy(key, "ota_server_cert"); + strcat(key, index_suffix); + json[key] = cnt_parameters_array[i].ota_server_cert; + } + strcpy(key, "mqtt_server"); + strcat(key, index_suffix); + json[key] = cnt_parameters_array[i].mqtt_server; + strcpy(key, "mqtt_port"); + strcat(key, index_suffix); + json[key] = cnt_parameters_array[i].mqtt_port; + strcpy(key, "mqtt_user"); + strcat(key, index_suffix); + json[key] = cnt_parameters_array[i].mqtt_user; + strcpy(key, "mqtt_pass"); + strcat(key, index_suffix); + json[key] = cnt_parameters_array[i].mqtt_pass; + strcpy(key, "mqtt_broker_secure"); + strcat(key, index_suffix); + json[key] = cnt_parameters_array[i].isConnectionSecure; + strcpy(key, "mqtt_iscertvalid"); + strcat(key, index_suffix); + json[key] = cnt_parameters_array[i].isCertValidate; + strcpy(key, "valid_cnt"); + strcat(key, index_suffix); + json[key] = cnt_parameters_array[i].validConnection; + } + } + + if (cnt_parameters_array[cnt_index].validConnection) { + json["cnt_index"] = cnt_index; + } + json["mqtt_topic"] = mqtt_topic; json["gateway_name"] = gateway_name; - json["mqtt_broker_secure"] = mqtt_secure; - json["mqtt_broker_cert"] = mqtt_cert; - json["mqtt_ss_index"] = mqtt_ss_index; - json["ota_server_cert"] = ota_server_cert; json["ota_pass"] = ota_pass; File configFile = SPIFFS.open("/config.json", "w"); @@ -1659,22 +1749,79 @@ bool loadConfigFromFlash() { } if (!json.isNull()) { Log.trace(F("\nparsed json, size: %u" CR), json.memoryUsage()); - if (json.containsKey("mqtt_server")) - strcpy(mqtt_server, json["mqtt_server"]); - if (json.containsKey("mqtt_port")) - strcpy(mqtt_port, json["mqtt_port"]); - if (json.containsKey("mqtt_user")) - strcpy(mqtt_user, json["mqtt_user"]); - if (json.containsKey("mqtt_pass")) - strcpy(mqtt_pass, json["mqtt_pass"]); + // Print json to serial port + //serializeJsonPretty(json, Serial); + + for (int i = 0; i < 3; ++i) { + char index_suffix[2]; // Large enough for 0, 1, or 2 and the null terminator + if (i == 0) { + index_suffix[0] = '\0'; // Empty string + } else { + index_suffix[0] = '0' + i; // Convert int to char + index_suffix[1] = '\0'; // Null terminator + } + char key[mqtt_key_max_size]; + strcpy(key, "mqtt_broker_cert"); + strcat(key, index_suffix); + if (json.containsKey(key)) { + cnt_parameters_array[i].server_cert = json[key].as(); + } + strcpy(key, "mqtt_client_cert"); + strcat(key, index_suffix); + if (json.containsKey(key)) { + cnt_parameters_array[i].client_cert = json[key].as(); + } + strcpy(key, "mqtt_client_key"); + strcat(key, index_suffix); + if (json.containsKey(key)) { + cnt_parameters_array[i].client_key = json[key].as(); + } + strcpy(key, "ota_server_cert"); + strcat(key, index_suffix); + if (json.containsKey(key)) { + cnt_parameters_array[i].ota_server_cert = json[key].as(); + } + strcpy(key, "mqtt_server"); + strcat(key, index_suffix); + if (json.containsKey(key)) { + strcpy(cnt_parameters_array[i].mqtt_server, json[key].as()); + } + strcpy(key, "mqtt_port"); + strcat(key, index_suffix); + if (json.containsKey(key)) { + strcpy(cnt_parameters_array[i].mqtt_port, json[key].as()); + } + strcpy(key, "mqtt_user"); + strcat(key, index_suffix); + if (json.containsKey(key)) { + strcpy(cnt_parameters_array[i].mqtt_user, json[key].as()); + } + strcpy(key, "mqtt_pass"); + strcat(key, index_suffix); + if (json.containsKey(key)) { + strcpy(cnt_parameters_array[i].mqtt_pass, json[key].as()); + } + strcpy(key, "mqtt_broker_secure"); + strcat(key, index_suffix); + if (json.containsKey(key)) { + cnt_parameters_array[i].isConnectionSecure = json[key].as(); + } + strcpy(key, "mqtt_iscertvalid"); + strcat(key, index_suffix); + if (json.containsKey(key)) { + cnt_parameters_array[i].isCertValidate = json[key].as(); + } + strcpy(key, "valid_cnt"); + strcat(key, index_suffix); + if (json.containsKey(key)) { + cnt_parameters_array[i].validConnection = json[key].as(); + } + } + if (json.containsKey("cnt_index")) { + cnt_index = json["cnt_index"].as(); + } if (json.containsKey("mqtt_topic")) strcpy(mqtt_topic, json["mqtt_topic"]); - if (json.containsKey("mqtt_broker_secure")) - mqtt_secure = json["mqtt_broker_secure"].as(); - if (json.containsKey("mqtt_broker_cert")) - mqtt_cert = json["mqtt_broker_cert"].as(); - if (json.containsKey("mqtt_ss_index")) - mqtt_ss_index = json["mqtt_ss_index"].as(); if (json.containsKey("gateway_name")) strcpy(gateway_name, json["gateway_name"]); if (json.containsKey("ota_pass")) { @@ -1689,8 +1836,6 @@ bool loadConfigFromFlash() { } # endif } - if (json.containsKey("ota_server_cert")) - ota_server_cert = json["ota_server_cert"].as(); result = true; } else { Log.warning(F("failed to load json config" CR)); @@ -1720,13 +1865,6 @@ void setupwifi(bool reset_settings) { if (reset_settings) eraseAndRestart(); -# if defined(ESP32) && defined(USE_BLUFI) - if (!wifi_reconnect_bypass()) { - startBlufi(); - return; - } -# endif - # ifdef USE_MAC_AS_GATEWAY_NAME String s = WiFi.macAddress(); snprintf(WifiManager_ssid, MAC_NAME_MAX_LEN, "%s_%.2s%.2s", Gateway_Short_Name, s.c_str(), s.c_str() + 3); @@ -1738,17 +1876,24 @@ void setupwifi(bool reset_settings) { // The extra parameters to be configured (can be either global or just in the setup) // After connecting, parameter.getValue() will get you the configured value - // id/name placeholder/prompt default length + // id/name placeholder/prompt default + // Index of connection parameters is 1 for onboarding parameters # ifndef WIFIMNG_HIDE_MQTT_CONFIG - WiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, parameters_size, " minlength='1' maxlength='64' required"); - WiFiManagerParameter custom_mqtt_port("port", "mqtt port", mqtt_port, 6, " minlength='1' maxlength='5' required"); - WiFiManagerParameter custom_mqtt_user("user", "mqtt user", mqtt_user, parameters_size, " maxlength='64'"); + WiFiManagerParameter custom_mqtt_server("server", "mqtt server", cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_server, parameters_size, " minlength='1' maxlength='64' required"); + WiFiManagerParameter custom_mqtt_port("port", "mqtt port", cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_port, 6, " minlength='1' maxlength='5' required"); + WiFiManagerParameter custom_mqtt_user("user", "mqtt user", cnt_parameters_array[CNT_DEFAULT_INDEX].mqtt_user, parameters_size, " maxlength='64'"); WiFiManagerParameter custom_mqtt_pass("pass", "mqtt pass", MQTT_PASS, parameters_size, " input type='password' maxlength='64'"); WiFiManagerParameter custom_mqtt_topic("topic", "mqtt base topic", mqtt_topic, mqtt_topic_max_size, " minlength='1' maxlength='64' required"); - WiFiManagerParameter custom_mqtt_secure("secure", "mqtt secure", "1", 2, mqtt_secure ? "type=\"checkbox\" checked" : "type=\"checkbox\""); - WiFiManagerParameter custom_mqtt_cert("cert", "
mqtt broker cert", mqtt_cert.c_str(), 4096); WiFiManagerParameter custom_gateway_name("name", "gateway name", gateway_name, parameters_size, " minlength='1' maxlength='64' required"); WiFiManagerParameter custom_ota_pass("ota", "gateway password", ota_pass, parameters_size, " input type='password' minlength='8' maxlength='64' required"); + WiFiManagerParameter custom_mqtt_secure("secure", "
mqtt secure", "1", 2, cnt_parameters_array[CNT_DEFAULT_INDEX].isConnectionSecure ? "type=\"checkbox\" checked" : "type=\"checkbox\""); + WiFiManagerParameter custom_validate_cert("validate", "
validate cert", "1", 2, cnt_parameters_array[CNT_DEFAULT_INDEX].isCertValidate ? "type=\"checkbox\" checked" : "type=\"checkbox\""); + WiFiManagerParameter custom_mqtt_cert("cert", "
mqtt server cert", cnt_parameters_array[CNT_DEFAULT_INDEX].server_cert.c_str(), 4096); + WiFiManagerParameter custom_ota_server_cert("ota_cert", "
ota server cert", cnt_parameters_array[CNT_DEFAULT_INDEX].ota_server_cert.c_str(), 4096); +# if MQTT_SECURE_SIGNED_CLIENT + WiFiManagerParameter custom_client_cert("client_cert", "
mqtt client cert", cnt_parameters_array[CNT_DEFAULT_INDEX].client_cert.c_str(), 4096); + WiFiManagerParameter custom_client_key("client_key", "
mqtt client key", cnt_parameters_array[CNT_DEFAULT_INDEX].client_key.c_str(), 4096); +# endif # endif //WiFiManager //Local intialization. Once its business is done, there is no need to keep it around @@ -1780,11 +1925,17 @@ void setupwifi(bool reset_settings) { wifiManager.addParameter(&custom_mqtt_port); wifiManager.addParameter(&custom_mqtt_user); wifiManager.addParameter(&custom_mqtt_pass); - wifiManager.addParameter(&custom_mqtt_secure); - wifiManager.addParameter(&custom_mqtt_cert); wifiManager.addParameter(&custom_gateway_name); wifiManager.addParameter(&custom_mqtt_topic); wifiManager.addParameter(&custom_ota_pass); + wifiManager.addParameter(&custom_ota_server_cert); + wifiManager.addParameter(&custom_mqtt_secure); + wifiManager.addParameter(&custom_validate_cert); + wifiManager.addParameter(&custom_mqtt_cert); +# if MQTT_SECURE_SIGNED_CLIENT + wifiManager.addParameter(&custom_client_cert); + wifiManager.addParameter(&custom_client_key); +# endif # endif //set minimum quality of signal so it ignores AP's under that quality wifiManager.setMinimumSignalQuality(MinimumWifiSignalQuality); @@ -1803,8 +1954,12 @@ void setupwifi(bool reset_settings) { wifiManager.setBreakAfterConfig(true); // If ethernet is used, we don't want to block the connection by keeping the portal up # endif - if (!wifi_reconnect_bypass()) // if we didn't connect with saved credential we start Wifimanager web portal + if (!wifi_reconnect_bypass()) // if we didn't connect with saved credential we start Wifimanager web portal / Blufi { + +# if defined(ESP32) && defined(USE_BLUFI) + startBlufi(); +# endif # ifdef ESP32 if (lowpowermode < 2) { displayPrint("Connect your phone to WIFI AP:", WifiManager_ssid, ota_pass); @@ -1835,10 +1990,12 @@ void setupwifi(bool reset_settings) { esp_wifi_get_config(WIFI_IF_AP, &conf); conf.ap.ssid_hidden = 1; esp_wifi_set_config(WIFI_IF_AP, &conf); -# endif - - //restart and try again + if (!blufiConnectAP) { + ESPRestart(3); // Restart if not connecting with BLUFI + } +# else ESPRestart(3); +# endif } } InfoIndicatorOFF(); @@ -1849,14 +2006,15 @@ void setupwifi(bool reset_settings) { if (shouldSaveConfig) { //read updated parameters + cnt_index = 1; # ifndef WIFIMNG_HIDE_MQTT_CONFIG - strcpy(mqtt_server, custom_mqtt_server.getValue()); - strcpy(mqtt_port, custom_mqtt_port.getValue()); - strcpy(mqtt_user, custom_mqtt_user.getValue()); + strcpy(cnt_parameters_array[cnt_index].mqtt_server, custom_mqtt_server.getValue()); + strcpy(cnt_parameters_array[cnt_index].mqtt_port, custom_mqtt_port.getValue()); + strcpy(cnt_parameters_array[cnt_index].mqtt_user, custom_mqtt_user.getValue()); // Check if the MQTT password field contains the default value if (strcmp(custom_mqtt_pass.getValue(), MQTT_PASS) != 0) { // If it's not the default password, update the MQTT password - strcpy(mqtt_pass, custom_mqtt_pass.getValue()); + strcpy(cnt_parameters_array[cnt_index].mqtt_pass, custom_mqtt_pass.getValue()); } strcpy(mqtt_topic, custom_mqtt_topic.getValue()); if (mqtt_topic[strlen(mqtt_topic) - 1] != '/' && strlen(mqtt_topic) < parameters_size) { @@ -1865,26 +2023,16 @@ void setupwifi(bool reset_settings) { strcpy(gateway_name, custom_gateway_name.getValue()); strcpy(ota_pass, custom_ota_pass.getValue()); - mqtt_secure = *custom_mqtt_secure.getValue(); - - int cert_len = strlen(custom_mqtt_cert.getValue()); - if (cert_len) { - char* cert_in = (char*)custom_mqtt_cert.getValue(); - while (*cert_in == ' ' || *cert_in == '\t') { - cert_in++; - } - - char* cert_begin = cert_in; - while (*cert_in != NULL) { - if (*cert_in == ' ' && (strncmp((cert_in + 1), "CERTIFICATE", 11) != 0)) { - *cert_in = '\n'; - } - cert_in++; - } - - mqtt_cert = cert_begin; - } + cnt_parameters_array[cnt_index].isConnectionSecure = *custom_mqtt_secure.getValue(); + cnt_parameters_array[cnt_index].isCertValidate = *custom_validate_cert.getValue(); + cnt_parameters_array[cnt_index].server_cert = processCert(custom_mqtt_cert.getValue()); + cnt_parameters_array[cnt_index].ota_server_cert = processCert(custom_ota_server_cert.getValue()); +# if MQTT_SECURE_SIGNED_CLIENT + cnt_parameters_array[cnt_index].client_cert = processCert(custom_client_cert.getValue()); + cnt_parameters_array[cnt_index].client_key = processCert(custom_client_key.getValue()); +# endif # endif + //save the custom parameters to FS saveConfig(); } @@ -1969,7 +2117,6 @@ void connectMQTTmdns() { } } #endif - void loop() { #ifndef ESPWifiManualSetup checkButton(); // check if a reset of wifi/mqtt settings is asked @@ -2329,11 +2476,13 @@ String stateMeasures() { } #endif SYSdata["freemem"] = freeMem; - SYSdata["mqttp"] = mqtt_port; - SYSdata["mqtts"] = mqtt_secure; + SYSdata["mqttp"] = cnt_parameters_array[cnt_index].mqtt_port; + SYSdata["mqtts"] = cnt_parameters_array[cnt_index].isConnectionSecure; + SYSdata["mqttv"] = cnt_parameters_array[cnt_index].isCertValidate; SYSdata["msgprc"] = queueLengthSum; SYSdata["msgblck"] = blockedMessages; SYSdata["maxq"] = maxQueueLength; + SYSdata["cnt_index"] = cnt_index; #ifdef ESP32 minFreeMem = ESP.getMinFreeHeap(); SYSdata["minmem"] = minFreeMem; @@ -2410,7 +2559,7 @@ String stateMeasures() { /** * Store signal values from RF, IR, SRFB or Weather stations so as to avoid duplicates */ -void storeSignalValue(SIGNAL_SIZE_UL_ULL MQTTvalue) { +void storeSignalValue(uint64_t MQTTvalue) { unsigned long now = millis(); // find oldest value of the buffer int o = getMin(); @@ -2445,7 +2594,7 @@ int getMin() { /** * Check if signal values from RF, IR, SRFB or Weather stations are duplicates */ -bool isAduplicateSignal(SIGNAL_SIZE_UL_ULL value) { +bool isAduplicateSignal(uint64_t value) { Log.trace(F("isAdupl?" CR)); for (int i = 0; i < struct_size; i++) { if (receivedSignal[i].value == value) { @@ -2461,7 +2610,7 @@ bool isAduplicateSignal(SIGNAL_SIZE_UL_ULL value) { #endif void receivingMQTT(char* topicOri, char* datacallback) { - StaticJsonDocument jsonBuffer; + StaticJsonDocument jsonBuffer; JsonObject jsondata = jsonBuffer.to(); auto error = deserializeJson(jsonBuffer, datacallback); if (error) { @@ -2471,7 +2620,7 @@ void receivingMQTT(char* topicOri, char* datacallback) { #if defined(ZgatewayRF) || defined(ZgatewayIR) || defined(ZgatewaySRFB) || defined(ZgatewayWeatherStation) if (strstr(topicOri, subjectMultiGTWKey) != NULL) { // storing received value so as to avoid publishing this value if it has been already sent by this or another OpenMQTTGateway - SIGNAL_SIZE_UL_ULL data = jsondata.isNull() ? STRTO_UL_ULL(datacallback, NULL, 10) : jsondata["value"]; + uint64_t data = jsondata.isNull() ? strtoull(datacallback, NULL, 10) : jsondata["value"]; if (data != 0 && !isAduplicateSignal(data)) { storeSignalValue(data); } @@ -2482,7 +2631,7 @@ void receivingMQTT(char* topicOri, char* datacallback) { // log the received json String buffer = ""; serializeJson(jsondata, buffer); - Log.notice(F("[ MQTT->OMG ]: %s" CR), buffer.c_str()); + //Log.notice(F("[ MQTT->OMG ]: %s" CR), buffer.c_str()); #ifdef ZgatewayPilight // ZgatewayPilight is only defined with json publishing due to its numerous parameters MQTTtoPilight(topicOri, jsondata); @@ -2699,11 +2848,12 @@ void MQTTHttpsFWUpdate(char* topicOri, JsonObject& HttpsFwUpdateData) { jsondata["origin"] = subjectRLStoMQTT; handleJsonEnqueue(jsondata); - const char* ota_cert = HttpsFwUpdateData["server_cert"]; - if (!ota_cert && !strstr(url, "http:")) { - if (ota_server_cert.length() > 0) { - Log.notice(F("Using stored cert" CR)); - ota_cert = ota_server_cert.c_str(); + std::string ota_cert = processCert(HttpsFwUpdateData["ota_server_cert"] | ""); + Log.notice(F("OTA cert: %s" CR), ota_cert.c_str()); + if (ota_cert.length() < MIN_CERT_LENGTH && !strstr(url, "http:")) { + if (cnt_parameters_array[cnt_index].ota_server_cert.length() > MIN_CERT_LENGTH) { + Log.notice(F("Using memory cert" CR)); + ota_cert = cnt_parameters_array[cnt_index].ota_server_cert.c_str(); } else { Log.notice(F("Using config cert" CR)); ota_cert = OTAserver_cert; @@ -2724,7 +2874,7 @@ void MQTTHttpsFWUpdate(char* topicOri, JsonObject& HttpsFwUpdateData) { } else { WiFiClientSecure update_client; - if (mqtt_secure) { + if (cnt_parameters_array[cnt_index].isConnectionSecure) { client.disconnect(); update_client = *(WiFiClientSecure*)eClient; } else { @@ -2732,13 +2882,13 @@ void MQTTHttpsFWUpdate(char* topicOri, JsonObject& HttpsFwUpdateData) { } # ifdef ESP32 - update_client.setCACert(ota_cert); + update_client.setCACert(ota_cert.c_str()); update_client.setTimeout(12); httpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); httpUpdate.rebootOnUpdate(false); result = httpUpdate.update(update_client, url); # elif ESP8266 - caCert.append(ota_cert); + caCert.append(ota_cert.c_str()); update_client.setTrustAnchors(&caCert); update_client.setTimeout(12000); ESPhttpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); @@ -2766,7 +2916,8 @@ void MQTTHttpsFWUpdate(char* topicOri, JsonObject& HttpsFwUpdateData) { jsondata["installed_version"] = latestVersion; jsondata["origin"] = subjectRLStoMQTT; handleJsonEnqueue(jsondata); - ota_server_cert = ota_cert; + if (cnt_index != 0) // We don't enable the change of cert provided at build time + cnt_parameters_array[cnt_index].ota_server_cert = ota_cert; # ifndef ESPWifiManualSetup saveConfig(); # endif @@ -2783,6 +2934,59 @@ void MQTTHttpsFWUpdate(char* topicOri, JsonObject& HttpsFwUpdateData) { } #endif +#ifdef ESP32 +# include "mbedtls/sha256.h" + +std::string generateHash(const std::string& input) { + unsigned char hash[32]; + mbedtls_sha256((unsigned char*)input.c_str(), input.length(), hash, 0); + + char hashString[65]; // Room for null terminator + for (int i = 0; i < 32; ++i) { + sprintf(&hashString[i * 2], "%02x", hash[i]); + } + + return std::string(hashString); +} +#else +std::string generateHash(const std::string& input) { + return "Not implemented for ESP8266"; +} +#endif +/** + * Read the certificates from the memory and publish a hash of the cert to the broker for identification purposes +*/ +void readCntParameters(int index) { + if (index < 0 || index > 2) { + Log.error(F("Invalid cnt index" CR)); + return; + } + StaticJsonDocument jsonBuffer; + JsonObject jsondata = jsonBuffer.to(); + jsondata["cnt_index"] = index; + jsondata["valid_cnt"] = cnt_parameters_array[index].validConnection; + if (cnt_parameters_array[index].server_cert.length() > MIN_CERT_LENGTH) { + jsondata["mqtt_server_cert_hash"] = generateHash(cnt_parameters_array[index].server_cert); + } + if (cnt_parameters_array[index].client_cert.length() > MIN_CERT_LENGTH) { + jsondata["mqtt_client_cert_hash"] = generateHash(cnt_parameters_array[index].client_cert); + } + if (cnt_parameters_array[index].client_key.length() > MIN_CERT_LENGTH) { + jsondata["mqtt_client_key_hash"] = generateHash(cnt_parameters_array[index].client_key); + } + if (cnt_parameters_array[index].ota_server_cert.length() > MIN_CERT_LENGTH) { + jsondata["ota_server_cert_hash"] = generateHash(cnt_parameters_array[index].ota_server_cert); + } + jsondata["mqtt_server"] = cnt_parameters_array[index].mqtt_server; + jsondata["mqtt_port"] = cnt_parameters_array[index].mqtt_port; + jsondata["mqtt_user"] = cnt_parameters_array[index].mqtt_user; + jsondata["mqtt_pass"] = generateHash(cnt_parameters_array[index].mqtt_pass); + jsondata["mqtt_secure"] = cnt_parameters_array[index].isConnectionSecure; + jsondata["mqtt_validate"] = cnt_parameters_array[index].isCertValidate; + + pub(subjectSYStoMQTT, jsondata); +} + void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding if (cmpToMainTopic(topicOri, subjectMQTTtoSYSset)) { bool restartESP = false; @@ -2823,7 +3027,7 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding stateMeasures(); } #endif - if (SYSdata.containsKey("wifi_ssid") && SYSdata.containsKey("wifi_pass")) { + if (SYSdata.containsKey("wifi_ssid") && SYSdata["wifi_ssid"].is() && SYSdata.containsKey("wifi_pass") && SYSdata["wifi_pass"].is()) { #ifdef ESP32 ProcessLock = true; # ifdef ZgatewayBT @@ -2855,7 +3059,9 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding bool disconnectClient = false; // Trigger client.disconnet if a user/password change doesn't - if (SYSdata.containsKey("mqtt_topic") || SYSdata.containsKey("gateway_name") || SYSdata.containsKey("gw_pass")) { + if ((SYSdata.containsKey("mqtt_topic") && SYSdata["mqtt_topic"].is()) || + (SYSdata.containsKey("gateway_name") && SYSdata["gateway_name"].is()) || + (SYSdata.containsKey("gw_pass") && SYSdata["gw_pass"].is())) { if (SYSdata.containsKey("mqtt_topic")) { strncpy(mqtt_topic, SYSdata["mqtt_topic"], parameters_size); } @@ -2873,100 +3079,153 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding } #ifdef MQTTsetMQTT - if (SYSdata.containsKey("mqtt_user") && SYSdata.containsKey("mqtt_pass")) { - bool update_server = false; - bool secure_connect = SYSdata["mqtt_secure"].as(); - void* prev_client = nullptr; - bool use_ss_cert = SYSdata.containsKey("mqtt_cert_index"); - uint8_t cert_index = mqtt_ss_index; - if (SYSdata.containsKey("mqtt_server") && SYSdata.containsKey("mqtt_port")) { - if (!SYSdata.containsKey("mqtt_secure")) { - Log.error(F("mqtt_server provided without mqtt_secure defined - ignoring command" CR)); - return; - } -# if MQTT_SECURE_SELF_SIGNED - if (use_ss_cert) { - cert_index = SYSdata["mqtt_cert_index"].as(); - if (cert_index >= sizeof(certs_array) / sizeof(ss_certs)) { - Log.error(F("mqtt_cert_index invalid - ignoring command" CR)); - return; - } - } -# endif + bool save_cnt = false; + bool read_cnt = false; + bool test_cnt = false; + if (SYSdata.containsKey("save_cnt") && SYSdata["save_cnt"].is()) { + save_cnt = SYSdata["save_cnt"].as(); + } + if (SYSdata.containsKey("read_cnt") && SYSdata["read_cnt"].is()) { + read_cnt = SYSdata["read_cnt"].as(); + } + if (SYSdata.containsKey("test_cnt") && SYSdata["test_cnt"].is()) { + test_cnt = SYSdata["test_cnt"].as(); + } -# ifdef ESP32 - ProcessLock = true; -# ifdef ZgatewayBT - stopProcessing(); -# endif -# endif - disconnectClient = false; - client.disconnect(); - update_server = true; - if (secure_connect != mqtt_secure) { - prev_client = eClient; - if (!mqtt_secure) { - eClient = new WiFiClientSecure; - } else { - Log.warning(F("Switching to unsecure MQTT broker" CR)); - eClient = new WiFiClient; - } + int prev_cnt_index = cnt_index; + char prev_mqtt_user[parameters_size]; + strcpy(prev_mqtt_user, cnt_parameters_array[prev_cnt_index].mqtt_user); + char prev_mqtt_pass[parameters_size]; + strcpy(prev_mqtt_pass, cnt_parameters_array[prev_cnt_index].mqtt_pass); + char prev_mqtt_server[parameters_size]; + strcpy(prev_mqtt_server, cnt_parameters_array[prev_cnt_index].mqtt_server); + char prev_mqtt_port[6]; + strcpy(prev_mqtt_port, cnt_parameters_array[prev_cnt_index].mqtt_port); + bool prev_mqtt_secure = cnt_parameters_array[prev_cnt_index].isConnectionSecure; - client.setClient(*(Client*)eClient); - } + if (SYSdata.containsKey("cnt_index") && SYSdata["cnt_index"].is()) { + if (SYSdata["cnt_index"].as() < 0 || SYSdata["cnt_index"].as() > 2) { + Log.error(F("Invalid cnt index provided - ignoring command" CR)); + return; + } + cnt_index = SYSdata["cnt_index"].as(); + Log.notice(F("MQTT cnt index %d" CR), cnt_index); - if (secure_connect) { - setupTLS(use_ss_cert, cert_index); - } - - client.setServer(SYSdata["mqtt_server"].as(), SYSdata["mqtt_port"].as()); - } else { -# ifdef ESP32 - ProcessLock = true; -# ifdef ZgatewayBT - stopProcessing(); -# endif -# endif - disconnectClient = false; - client.disconnect(); + if (SYSdata.containsKey("mqtt_user") && SYSdata["mqtt_user"].is() && SYSdata.containsKey("mqtt_pass") && SYSdata["mqtt_pass"].is()) { + strcpy(cnt_parameters_array[cnt_index].mqtt_user, SYSdata["mqtt_user"]); + strcpy(cnt_parameters_array[cnt_index].mqtt_pass, SYSdata["mqtt_pass"]); + cnt_parameters_array[cnt_index].validConnection = false; } - String prev_user = mqtt_user; - String prev_pass = mqtt_pass; - strcpy(mqtt_user, SYSdata["mqtt_user"]); - strcpy(mqtt_pass, SYSdata["mqtt_pass"]); + if (SYSdata.containsKey("mqtt_server") && SYSdata.containsKey("mqtt_port") && SYSdata["mqtt_port"].is() && SYSdata["mqtt_server"].is()) { + strcpy(cnt_parameters_array[cnt_index].mqtt_server, SYSdata["mqtt_server"]); + strcpy(cnt_parameters_array[cnt_index].mqtt_port, SYSdata["mqtt_port"]); + cnt_parameters_array[cnt_index].validConnection = false; + } + if (SYSdata.containsKey("mqtt_secure") && SYSdata["mqtt_secure"].is()) { + cnt_parameters_array[cnt_index].isConnectionSecure = SYSdata["mqtt_secure"].as(); + cnt_parameters_array[cnt_index].validConnection = false; + } + + if (SYSdata.containsKey("mqtt_validate") && SYSdata["mqtt_validate"].is()) { + cnt_parameters_array[cnt_index].isCertValidate = SYSdata["mqtt_validate"].as(); + cnt_parameters_array[cnt_index].validConnection = false; + } + + // Copy the certs to the memory + if (SYSdata.containsKey("mqtt_server_cert") && SYSdata["mqtt_server_cert"].is()) { + cnt_parameters_array[cnt_index].server_cert = processCert(SYSdata["mqtt_server_cert"].as()); + Log.trace(F("Assigning server cert %s" CR), generateHash(cnt_parameters_array[cnt_index].server_cert).c_str()); + cnt_parameters_array[cnt_index].validConnection = false; + } + if (SYSdata.containsKey("mqtt_client_cert") && SYSdata["mqtt_client_cert"].is()) { + cnt_parameters_array[cnt_index].client_cert = processCert(SYSdata["mqtt_client_cert"].as()); + Log.trace(F("Assigning client cert %s" CR), generateHash(cnt_parameters_array[cnt_index].client_cert).c_str()); + cnt_parameters_array[cnt_index].validConnection = false; + } + if (SYSdata.containsKey("mqtt_client_key") && SYSdata["mqtt_client_key"].is()) { + cnt_parameters_array[cnt_index].client_key = processCert(SYSdata["mqtt_client_key"].as()); + Log.trace(F("Assigning client key %s" CR), generateHash(cnt_parameters_array[cnt_index].client_key).c_str()); + cnt_parameters_array[cnt_index].validConnection = false; + } + if (SYSdata.containsKey("ota_server_cert") && SYSdata["ota_server_cert"].is()) { + cnt_parameters_array[cnt_index].ota_server_cert = processCert(SYSdata["ota_server_cert"].as()); + Log.trace(F("Assigning OTA server cert %s" CR), generateHash(cnt_parameters_array[cnt_index].ota_server_cert).c_str()); + } + // Read the memory certs hash to MQTT + if (read_cnt) + readCntParameters(cnt_index); + } + + // Change of mqtt secure connection or certs + void* prev_client = nullptr; + + if (save_cnt || test_cnt) { + // Stop the processing/disconnect +# ifdef ESP32 + ProcessLock = true; +# ifdef ZgatewayBT + stopProcessing(); +# endif +# endif + client.disconnect(); + + prev_client = eClient; + + if (cnt_parameters_array[cnt_index].isConnectionSecure) { + Log.notice(F("Connecting to secure MQTT broker" CR)); + eClient = new WiFiClientSecure; + } else { + Log.warning(F("Connecting to unsecure MQTT broker" CR)); + eClient = new WiFiClient; + } + client.setClient(*(Client*)eClient); + + if (cnt_parameters_array[cnt_index].isConnectionSecure) { + setupTLS(cnt_index); + } + } + + if (save_cnt || (test_cnt && !read_cnt)) { + Log.notice(F("Attempting connection to new MQTT broker" CR)); + client.setServer(cnt_parameters_array[cnt_index].mqtt_server, strtol(cnt_parameters_array[cnt_index].mqtt_port, NULL, 10)); + // Connect the client connectMQTT(); - + // If the connection is successful we save the parameters to the flash memory if (client.connected()) { - if (update_server) { - strcpy(mqtt_server, SYSdata["mqtt_server"]); - strcpy(mqtt_port, SYSdata["mqtt_port"]); - mqtt_ss_index = cert_index; - if (prev_client != nullptr) { - mqtt_secure = !mqtt_secure; - delete prev_client; - } + Log.notice(F("Connection successful" CR)); + cnt_parameters_array[cnt_index].validConnection = true; + readCntParameters(cnt_index); + + if (prev_client != nullptr) { + delete prev_client; } # ifndef ESPWifiManualSetup - saveConfig(); + if (save_cnt) // Save the new parameters to the flash + saveConfig(); # endif - } else { - if (update_server) { - if (prev_client != nullptr) { - delete eClient; - eClient = prev_client; - client.setClient(*(Client*)eClient); - } - uint16_t port = strtol(mqtt_port, NULL, 10); - client.setServer(mqtt_server, port); + } else { // Revert to previous settings + Log.error(F("Connection failed, reverting to previous settings" CR)); + cnt_index = prev_cnt_index; + if (SYSdata.containsKey("mqtt_user") && SYSdata.containsKey("mqtt_pass")) { + strcpy(cnt_parameters_array[cnt_index].mqtt_user, prev_mqtt_user); + strcpy(cnt_parameters_array[cnt_index].mqtt_pass, prev_mqtt_pass); } - strcpy(mqtt_user, prev_user.c_str()); - strcpy(mqtt_pass, prev_pass.c_str()); - if (mqtt_secure) { - setupTLS(MQTT_SECURE_SELF_SIGNED, mqtt_ss_index); + if (SYSdata.containsKey("mqtt_server") && SYSdata.containsKey("mqtt_port")) { + client.setServer(prev_mqtt_server, strtol(prev_mqtt_port, NULL, 10)); } + // Revert the change of secure connection + if (SYSdata.containsKey("mqtt_secure")) { + cnt_parameters_array[cnt_index].isConnectionSecure = prev_mqtt_secure; + } + if (prev_client != nullptr) { + delete eClient; + eClient = prev_client; + client.setClient(*(Client*)eClient); + } + // Restest the connection connectMQTT(); } restartESP = true; @@ -2977,7 +3236,10 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding client.disconnect(); } -#ifdef ZmqttDiscovery + if (SYSdata.containsKey("xtomqtt") && SYSdata["xtomqtt"].is()) { + SYSConfig.XtoMQTT = SYSdata["xtomqtt"]; + Log.notice(F("xtomqtt: %T" CR), SYSConfig.XtoMQTT); + } if (SYSdata.containsKey("disc")) { if (SYSdata["disc"].is()) { if (SYSdata["disc"] == true && SYSConfig.discovery == false) @@ -2991,15 +3253,16 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding } Log.notice(F("Discovery state: %T" CR), SYSConfig.discovery); } -# ifdef ESP32 +#ifdef ESP32 if (SYSdata.containsKey("save") && SYSdata["save"].as()) { StaticJsonDocument jsonBuffer; JsonObject jo = jsonBuffer.to(); jo["disc"] = SYSConfig.discovery; jo["ohdisc"] = SYSConfig.ohdiscovery; -# ifdef RGB_INDICATORS + jo["xtomqtt"] = SYSConfig.XtoMQTT; +# ifdef RGB_INDICATORS jo["rgbb"] = SYSConfig.rgbbrightness; -# endif +# endif // Save config into NVS (non-volatile storage) String conf = ""; serializeJson(jsonBuffer, conf); @@ -3008,7 +3271,6 @@ void MQTTtoSYS(char* topicOri, JsonObject& SYSdata) { // json object decoding preferences.end(); Log.notice(F("SYS Config_save: %s, result: %d" CR), conf.c_str(), result); } -# endif #endif if (restartESP) { ESPRestart(7); diff --git a/platformio.ini b/platformio.ini index ba1d2615..4d85dabd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -84,7 +84,6 @@ extra_configs = ;default_envs = nodemcuv2-rf-cc1101 ;default_envs = nodemcuv2-somfy-cc1101 ;default_envs = manual-wifi-test -;default_envs = nodemcuv2-mqtt-fw-test ;default_envs = rf-wifi-gateway ;default_envs = nodemcuv2-rf2 ;default_envs = nodemcuv2-rf2-cc1101