diff --git a/.travis.yml b/.travis.yml index 83dc99ed01..52abd9ec1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,8 +45,8 @@ env: - ENV=minimal_IRext_ESP8266_1M - ENV=minimal_IRext_ESP8266_4M1M - ENV=minimal_IRext_ESP8266_4M2M - - ENV=minimal_core_242_ESP8266_1M_OTA - - ENV=minimal_core_242_ESP8285_1M_OTA + #- ENV=minimal_core_242_ESP8266_1M_OTA + #- ENV=minimal_core_242_ESP8285_1M_OTA - ENV=minimal_core_274_ESP8266_1M_OTA_Domoticz - ENV=minimal_core_274_ESP8266_1M_OTA_FHEM_HA - ENV=minimal_core_274_ESP8285_1M_OTA_Domoticz @@ -80,7 +80,7 @@ env: - ENV=test_ESP8266_4M1M_VCC #- ENV=test_ESP8266_4M1M_VCC_MDNS_SD - ENV=test_alt_wifi_ESP8266_4M1M_VCC - - ENV=test_beta_ESP8266_16M_LittleFS + #- ENV=test_beta_ESP8266_16M_LittleFS - ENV=test_beta_ESP8266_4M1M script: diff --git a/docs/source/Controller/C011.rst b/docs/source/Controller/C011.rst index 8052f14ac8..d37662a46e 100644 --- a/docs/source/Controller/C011.rst +++ b/docs/source/Controller/C011.rst @@ -37,3 +37,61 @@ Change log Description ----------- + +Many systems provide a simple HTTP API where data can be changed using a HTTP request with a properly constructed querystring. + +This Controller allows you to send HTTP GET, POST and PUT Requests to the given webserver. + +For every device you have to choose to which Controllers it is sending its data. When configuring the Controller you can access this data and use them to generate your querystring. + +You can use this placeholders in http header and in the http body: + +* ``%systime%`` will be replaced with the local system time +* ``%vcc%`` will be replaced with the power supply voltage (if enable in this build) +* ``%ip%`` will be replaced with the local ip adress +* ``%sysload%`` will be replaced with the system load +* ``%uptime%`` will be replaced with the system uptime +* ``%CR%`` will be replaced with "\r" +* ``%LF%`` will be replaced with "\n" (newline) +* ``%sysname%`` will be replaced with the system name +* ``%tskname%`` will be replaced with the name of the device which is sending data to this controller +* ``%id%`` will be replaced with IDX value. +* ``%vname1%`` will be replaced with Valuename 1 +* ``%vname2%`` will be replaced with Valuename 2 +* ``%vname3%`` will be replaced with Valuename 3 +* ``%vname4%`` will be replaced with Valuename 4 +* ``%val1%`` will be replaced with the value 1 of the device which is sending data to this controller +* ``%val2%`` will be replaced with the value 2 of the device which is sending data to this controller + +You can also write things like this: + +``%1%_some_text_and_placeholder_%/1%`` everything between ``%1%`` and ``%/1%`` will only print when there is a value ``1`` + + +Examples +-------- + +InfluxDB HTTP Api +^^^^^^^^^^^^^^^^^ + +* HTTP Method: ``POST`` +* HTTP URI: ``write?db=testdb`` +* HTTP Header: ``Content-Type: application/x-www-form-urlencoded`` +* HTTP Body: + +.. code-block:: none + + %1%%vname1%,Standort=%tskname% + Wert=%val1%%/1%%2%%LF%%vname2%,Standort=%taskname% + Wert=%val2%%/2%%3%%LF%%vname2%,Standort=%taskname% + Wert=%val3%%/3%%4%%LF%%vname2%,Standort=%taskname% + Wert=%val4%%/4% + +See also `InfluxDB API description `_ + +Nettemp HTTP Api +^^^^^^^^^^^^^^^^ + +untested but should work with something like this: + +``/receiver.php?device=ip&type=%1%%vname1%%/1%%2%;%vname2%%/2%%3%;%vname3%%/3%%4%;%vname4%%/4%&value=%1%%val1%%/1%%2%;%val2%%/2%%3%;%val3%%/3%%4%;%val4%%/4%`` \ No newline at end of file diff --git a/docs/source/Controller/_Controller.rst b/docs/source/Controller/_Controller.rst index 8a88239d81..cdc80d9207 100644 --- a/docs/source/Controller/_Controller.rst +++ b/docs/source/Controller/_Controller.rst @@ -67,6 +67,11 @@ before WiFi connection is made or during lost connection. Especially useful for controllers which cannot send samples in a burst. This makes the receiving time stamp useless to detect what samples were taken around the same time. The sample set counter value can help matching received samples to a single set. +.. note:: + Be careful when setting the timeout too high. + For almost all controllers, sending data is a blocking call, so it may halt execution of other code on the node. + With timouts longer than 2 seconds, the ESP may reboot as the software watchdog may step in. + Sample ThingSpeak configuration diff --git a/docs/source/Controller/_controller_substitutions.repl b/docs/source/Controller/_controller_substitutions.repl index 7bbefccd00..46555d7463 100644 --- a/docs/source/Controller/_controller_substitutions.repl +++ b/docs/source/Controller/_controller_substitutions.repl @@ -111,9 +111,9 @@ .. |C010_maintainer| replace:: `.` .. |C010_compileinfo| replace:: `.` -.. |C011_name| replace:: :cyan:`Generic HTTP` +.. |C011_name| replace:: :cyan:`Generic HTTP Advanced` .. |C011_type| replace:: :cyan:`Controller` -.. |C011_typename| replace:: :cyan:`Controller - Generic HTTP` +.. |C011_typename| replace:: :cyan:`Controller - Generic HTTP Advanced` .. |C011_status| replace:: :yellow:`TESTING` .. |C011_github| replace:: C011.ino .. _C011_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C011.ino diff --git a/lib/RN2483-Arduino-Library/src/rn2xx3.cpp b/lib/RN2483-Arduino-Library/src/rn2xx3.cpp index 240b90a37a..eee40e7d43 100644 --- a/lib/RN2483-Arduino-Library/src/rn2xx3.cpp +++ b/lib/RN2483-Arduino-Library/src/rn2xx3.cpp @@ -160,6 +160,11 @@ rn2xx3_handler::RN_state rn2xx3::async_loop() return _rn2xx3_handler.get_state(); } +uint8_t rn2xx3::get_busy_count() const +{ + return _rn2xx3_handler.get_busy_count(); +} + rn2xx3_handler::RN_state rn2xx3::wait_command_finished(unsigned long timeout) { return _rn2xx3_handler.wait_command_finished(timeout); diff --git a/lib/RN2483-Arduino-Library/src/rn2xx3.h b/lib/RN2483-Arduino-Library/src/rn2xx3.h index a39291ef10..06bd5cfd60 100644 --- a/lib/RN2483-Arduino-Library/src/rn2xx3.h +++ b/lib/RN2483-Arduino-Library/src/rn2xx3.h @@ -206,6 +206,8 @@ class rn2xx3 { */ rn2xx3_handler::RN_state async_loop(); + uint8_t get_busy_count() const; + rn2xx3_handler::RN_state wait_command_finished(unsigned long timeout = 10000); rn2xx3_handler::RN_state wait_command_accepted(unsigned long timeout = 10000); diff --git a/lib/RN2483-Arduino-Library/src/rn2xx3_handler.cpp b/lib/RN2483-Arduino-Library/src/rn2xx3_handler.cpp index 143ecc0fdb..789544d7f6 100644 --- a/lib/RN2483-Arduino-Library/src/rn2xx3_handler.cpp +++ b/lib/RN2483-Arduino-Library/src/rn2xx3_handler.cpp @@ -678,6 +678,10 @@ rn2xx3_handler::RN_state rn2xx3_handler::get_state() const { return _state; } +uint8_t rn2xx3_handler::get_busy_count() const { + return _busy_count; +} + String rn2xx3_handler::sysver() { String ver = sendRawCommand(F("sys get ver")); @@ -1014,7 +1018,7 @@ void rn2xx3_handler::handle_reply_received() { } else { - delay(1000); + delay(100); } break; } diff --git a/lib/RN2483-Arduino-Library/src/rn2xx3_handler.h b/lib/RN2483-Arduino-Library/src/rn2xx3_handler.h index 1b4e01b540..3504a63656 100644 --- a/lib/RN2483-Arduino-Library/src/rn2xx3_handler.h +++ b/lib/RN2483-Arduino-Library/src/rn2xx3_handler.h @@ -181,6 +181,8 @@ class rn2xx3_handler { RN_state get_state() const; + uint8_t get_busy_count() const; + String sysver(); // delay from last moment of sending to receive RX1 and RX2 window diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index e45aa30d92..8e7ab8df62 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -143,6 +143,9 @@ platform_packages = [core_esp32_1_12_2] platform = espressif32@1.12.4 +[core_esp32_2_0_0] +platform = espressif32@2.0.0 + [core_esp32_stage] platform = https://github.com/platformio/platform-espressif32.git#feature/stage diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 70bf457f2c..a0c46fcbdc 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -8,7 +8,7 @@ [esp32_common] extends = common -platform = ${core_esp32_1_12_2.platform} +platform = ${core_esp32_2_0_0.platform} lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266HTTPUpdateServer, ESP8266mDNS, IRremoteESP8266, ESPEasy_ESP8266Ping, ESP32_ping, HeatpumpIR lib_deps = https://github.com/TD-er/ESPEasySerial.git#v2.0.3, Adafruit ILI9341, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO board_build.f_flash = 80000000L diff --git a/requirements.txt b/requirements.txt index fce7245695..fb629fc3a9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,7 +18,7 @@ MarkupSafe==1.1.1 marshmallow==3.7.1 packaging==20.4 pathtools==0.1.2 -platformio>=4.3.4 +platformio>=5.0.1 port-for==0.3.1 pycparser==2.20 pyelftools==0.26 diff --git a/src/Controller.ino b/src/Controller.ino index 162e74602d..20fd09f10f 100644 --- a/src/Controller.ino +++ b/src/Controller.ino @@ -153,6 +153,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) } updateMQTTclient_connected(); // mqtt = WiFiClient(); // workaround see: https://github.com/esp8266/Arduino/issues/4497#issuecomment-373023864 + yield(); mqtt.setTimeout(ControllerSettings.ClientTimeout); MQTTclient.setClient(mqtt); @@ -198,7 +199,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) byte controller_number = Settings.Protocol[controller_idx]; - count_connection_results(MQTTresult, F("MQTT : Broker "), controller_number, ControllerSettings); + count_connection_results(MQTTresult, F("MQTT : Broker "), controller_number); if (!MQTTresult) { MQTTclient.disconnect(); @@ -272,46 +273,55 @@ bool MQTTCheck(controllerIndex_t controller_idx) if (Protocol[ProtocolIndex].usesMQTT) { - MakeControllerSettings(ControllerSettings); - if (!AllocatedControllerSettings()) { - addLog(LOG_LEVEL_ERROR, F("MQTT : Cannot check, out of RAM")); - return false; - } + bool mqtt_sendLWT = false; + String LWTTopic, LWTMessageConnect; + bool willRetain = false; + { + MakeControllerSettings(ControllerSettings); + if (!AllocatedControllerSettings()) { + addLog(LOG_LEVEL_ERROR, F("MQTT : Cannot check, out of RAM")); + return false; + } + + LoadControllerSettings(controller_idx, ControllerSettings); - LoadControllerSettings(controller_idx, ControllerSettings); + // FIXME TD-er: Is this still needed? + /* + #ifdef USES_ESPEASY_NOW + if (!MQTTclient.connected()) { + if (ControllerSettings.enableESPEasyNowFallback()) { + return true; + } + } + #endif + */ - // FIXME TD-er: Is this still needed? - /* - #ifdef USES_ESPEASY_NOW - if (!MQTTclient.connected()) { - if (ControllerSettings.enableESPEasyNowFallback()) { + if (!ControllerSettings.isSet()) { return true; } + + if (ControllerSettings.mqtt_sendLWT()) { + mqtt_sendLWT = true; + LWTTopic = getLWT_topic(ControllerSettings); + LWTMessageConnect = getLWT_messageConnect(ControllerSettings); + willRetain = ControllerSettings.mqtt_willRetain(); + } } - #endif - */ - - if (ControllerSettings.isSet()) { - if (MQTTclient_should_reconnect || !MQTTclient.connected()) - { - if (MQTTclient_should_reconnect) { - addLog(LOG_LEVEL_ERROR, F("MQTT : Intentional reconnect")); - } - return MQTTConnect(controller_idx); + if (MQTTclient_should_reconnect || !MQTTclient.connected()) + { + if (MQTTclient_should_reconnect) { + addLog(LOG_LEVEL_ERROR, F("MQTT : Intentional reconnect")); } + return MQTTConnect(controller_idx); + } - if (MQTTclient_must_send_LWT_connected) { - if (ControllerSettings.mqtt_sendLWT()) { - String LWTTopic = getLWT_topic(ControllerSettings); - String LWTMessageConnect = getLWT_messageConnect(ControllerSettings); - bool willRetain = ControllerSettings.mqtt_willRetain(); - - if (MQTTclient.publish(LWTTopic.c_str(), LWTMessageConnect.c_str(), willRetain)) { - MQTTclient_must_send_LWT_connected = false; - } - } else { + if (MQTTclient_must_send_LWT_connected) { + if (mqtt_sendLWT) { + if (MQTTclient.publish(LWTTopic.c_str(), LWTMessageConnect.c_str(), willRetain)) { MQTTclient_must_send_LWT_connected = false; } + } else { + MQTTclient_must_send_LWT_connected = false; } } } diff --git a/src/WebServer_ControllerPage.ino b/src/WebServer_ControllerPage.ino index 4b719b9591..d8fdde4e26 100644 --- a/src/WebServer_ControllerPage.ino +++ b/src/WebServer_ControllerPage.ino @@ -23,6 +23,7 @@ void handle_controllers() { if ((protocol != -1) && !controllerNotSet) { bool mustInit = false; + bool mustCallCpluginSave = false; { // Place in a scope to free ControllerSettings memory ASAP MakeControllerSettings(ControllerSettings); @@ -48,10 +49,18 @@ void handle_controllers() { { mustInit = true; handle_controllers_CopySubmittedSettings(controllerindex, ControllerSettings); + mustCallCpluginSave = true; } } addHtmlError(SaveControllerSettings(controllerindex, ControllerSettings)); } + if (mustCallCpluginSave) { + // Call CPLUGIN_WEBFORM_SAVE after destructing ControllerSettings object to reduce RAM usage. + // Controller plugin almost only deals with custom controller settings. + // Even if they need to save things to the ControllerSettings, then the changes must + // already be saved first as the CPluginCall does not have the ControllerSettings as argument. + handle_controllers_CopySubmittedSettings_CPluginCall(controllerindex); + } addHtmlError(SaveSettings()); if (mustInit) { @@ -133,7 +142,9 @@ void handle_controllers_CopySubmittedSettings(byte controllerindex, ControllerSe ControllerSettingsStruct::VarType varType = static_cast(parameterIdx); saveControllerParameterForm(ControllerSettings, controllerindex, varType); } +} +void handle_controllers_CopySubmittedSettings_CPluginCall(byte controllerindex) { protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(controllerindex); if (validProtocolIndex(ProtocolIndex)) { @@ -257,108 +268,113 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex const protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(controllerindex); if (Settings.Protocol[controllerindex]) - { - MakeControllerSettings(ControllerSettings); - if (!AllocatedControllerSettings()) { - addHtmlError(F("Out of memory, cannot load page")); - } else { - LoadControllerSettings(controllerindex, ControllerSettings); - - if (!Protocol[ProtocolIndex].Custom) - { - if (Protocol[ProtocolIndex].usesHost) { - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_USE_DNS); + { + { + MakeControllerSettings(ControllerSettings); + if (!AllocatedControllerSettings()) { + addHtmlError(F("Out of memory, cannot load page")); + } else { + LoadControllerSettings(controllerindex, ControllerSettings); - if (ControllerSettings.UseDNS) - { - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_HOSTNAME); + if (!Protocol[ProtocolIndex].Custom) + { + if (Protocol[ProtocolIndex].usesHost) { + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_USE_DNS); + + if (ControllerSettings.UseDNS) + { + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_HOSTNAME); + } + else + { + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_IP); + } } - else - { - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_IP); + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_PORT); + + if (Protocol[ProtocolIndex].usesQueue) { + addTableSeparator(F("Controller Queue"), 2, 3); + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MIN_SEND_INTERVAL); + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MAX_QUEUE_DEPTH); + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MAX_RETRIES); + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_FULL_QUEUE_ACTION); } - } - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_PORT); - - if (Protocol[ProtocolIndex].usesQueue) { - addTableSeparator(F("Controller Queue"), 2, 3); - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MIN_SEND_INTERVAL); - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MAX_QUEUE_DEPTH); - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MAX_RETRIES); - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_FULL_QUEUE_ACTION); - } - if (Protocol[ProtocolIndex].usesCheckReply) { - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_CHECK_REPLY); - } + if (Protocol[ProtocolIndex].usesCheckReply) { + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_CHECK_REPLY); + } - if (Protocol[ProtocolIndex].usesTimeout) { - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_TIMEOUT); - } + if (Protocol[ProtocolIndex].usesTimeout) { + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_TIMEOUT); + } - if (Protocol[ProtocolIndex].usesSampleSets) { - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_SAMPLE_SET_INITIATOR); - } + if (Protocol[ProtocolIndex].usesSampleSets) { + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_SAMPLE_SET_INITIATOR); + } - if (Protocol[ProtocolIndex].useExtendedCredentials()) { - addTableSeparator(F("Credentials"), 2, 3); - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_USE_EXTENDED_CREDENTIALS); - } + if (Protocol[ProtocolIndex].useExtendedCredentials()) { + addTableSeparator(F("Credentials"), 2, 3); + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_USE_EXTENDED_CREDENTIALS); + } - if (Protocol[ProtocolIndex].usesAccount) - { - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_USER); - } + if (Protocol[ProtocolIndex].usesAccount) + { + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_USER); + } - if (Protocol[ProtocolIndex].usesPassword) - { - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_PASS); - } - #ifdef USES_MQTT - if (Protocol[ProtocolIndex].usesMQTT) { - addTableSeparator(F("MQTT"), 2, 3); - - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_CLIENT_ID); - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_UNIQUE_CLIENT_ID_RECONNECT); - addRowLabel(F("Current Client ID")); - addHtml(getMQTTclientID(ControllerSettings)); - addFormNote(F("Updated on load of this page")); - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_RETAINFLAG); - } - #endif // USES_MQTT + if (Protocol[ProtocolIndex].usesPassword) + { + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_PASS); + } + #ifdef USES_MQTT + if (Protocol[ProtocolIndex].usesMQTT) { + addTableSeparator(F("MQTT"), 2, 3); + + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_CLIENT_ID); + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_UNIQUE_CLIENT_ID_RECONNECT); + addRowLabel(F("Current Client ID")); + addHtml(getMQTTclientID(ControllerSettings)); + addFormNote(F("Updated on load of this page")); + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_RETAINFLAG); + } + #endif // USES_MQTT - if (Protocol[ProtocolIndex].usesTemplate || Protocol[ProtocolIndex].usesMQTT) - { - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_SUBSCRIBE); - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_PUBLISH); - } - #ifdef USES_MQTT - if (Protocol[ProtocolIndex].usesMQTT) - { - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_LWT_TOPIC); - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_LWT_CONNECT_MESSAGE); - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_LWT_DISCONNECT_MESSAGE); - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_SEND_LWT); - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_WILL_RETAIN); - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_CLEAN_SESSION); + if (Protocol[ProtocolIndex].usesTemplate || Protocol[ProtocolIndex].usesMQTT) + { + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_SUBSCRIBE); + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_PUBLISH); + } + #ifdef USES_MQTT + if (Protocol[ProtocolIndex].usesMQTT) + { + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_LWT_TOPIC); + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_LWT_CONNECT_MESSAGE); + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_LWT_DISCONNECT_MESSAGE); + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_SEND_LWT); + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_WILL_RETAIN); + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_CLEAN_SESSION); + } + #endif // USES_MQTT } - #endif // USES_MQTT } - { - // Load controller specific settings - struct EventStruct TempEvent; - TempEvent.ControllerIndex = controllerindex; + // End of scope for ControllerSettings, destruct it to save memory. + } + { + // Load controller specific settings + struct EventStruct TempEvent; + TempEvent.ControllerIndex = controllerindex; - String webformLoadString; - CPluginCall(ProtocolIndex, CPlugin::Function::CPLUGIN_WEBFORM_LOAD, &TempEvent, webformLoadString); + String webformLoadString; + CPluginCall(ProtocolIndex, CPlugin::Function::CPLUGIN_WEBFORM_LOAD, &TempEvent, webformLoadString); - if (webformLoadString.length() > 0) { - addHtmlError(F("Bug in CPlugin::Function::CPLUGIN_WEBFORM_LOAD, should not append to string, use addHtml() instead")); - } + if (webformLoadString.length() > 0) { + addHtmlError(F("Bug in CPlugin::Function::CPLUGIN_WEBFORM_LOAD, should not append to string, use addHtml() instead")); } - addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_ENABLED); } + // Separate enabled checkbox as it doesn't need to use the ControllerSettings. + // So ControllerSettings object can be destructed before controller specific settings are loaded. + addControllerEnabledForm(controllerindex); } addFormSeparator(2); diff --git a/src/_C009.ino b/src/_C009.ino index 6c81bab404..ef9604e8cb 100644 --- a/src/_C009.ino +++ b/src/_C009.ino @@ -78,12 +78,6 @@ bool CPlugin_009(CPlugin::Function function, struct EventStruct *event, String& byte valueCount = getValueCountFromSensorType(event->sensorType); C009_queue_element element(event); - MakeControllerSettings(ControllerSettings); - if (!AllocatedControllerSettings()) { - break; - } - LoadControllerSettings(event->ControllerIndex, ControllerSettings); - for (byte x = 0; x < valueCount; x++) { element.txt[x] = formatUserVarNoCheck(event, x); diff --git a/src/_C010.ino b/src/_C010.ino index e863b89ae8..b692859a39 100644 --- a/src/_C010.ino +++ b/src/_C010.ino @@ -64,23 +64,25 @@ bool CPlugin_010(CPlugin::Function function, struct EventStruct *event, String& PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummy); } - MakeControllerSettings(ControllerSettings); - if (!AllocatedControllerSettings()) { - break; - } - LoadControllerSettings(event->ControllerIndex, ControllerSettings); - - for (byte x = 0; x < valueCount; x++) { - bool isvalid; - String formattedValue = formatUserVar(event, x, isvalid); - if (isvalid) { - element.txt[x] = ""; - element.txt[x] += ControllerSettings.Publish; - parseControllerVariables(element.txt[x], event, false); - element.txt[x].replace(F("%valname%"), ExtraTaskSettings.TaskDeviceValueNames[x]); - element.txt[x].replace(F("%value%"), formattedValue); - addLog(LOG_LEVEL_DEBUG_MORE, element.txt[x]); + MakeControllerSettings(ControllerSettings); + if (!AllocatedControllerSettings()) { + break; + } + LoadControllerSettings(event->ControllerIndex, ControllerSettings); + + for (byte x = 0; x < valueCount; x++) + { + bool isvalid; + String formattedValue = formatUserVar(event, x, isvalid); + if (isvalid) { + element.txt[x] = ""; + element.txt[x] += ControllerSettings.Publish; + parseControllerVariables(element.txt[x], event, false); + element.txt[x].replace(F("%valname%"), ExtraTaskSettings.TaskDeviceValueNames[x]); + element.txt[x].replace(F("%value%"), formattedValue); + addLog(LOG_LEVEL_DEBUG_MORE, element.txt[x]); + } } } // FIXME TD-er must define a proper move operator diff --git a/src/_C011.ino b/src/_C011.ino index db227415c8..8341bd1d00 100644 --- a/src/_C011.ino +++ b/src/_C011.ino @@ -5,14 +5,17 @@ // ########################### Controller Plugin 011: Generic HTTP Advanced ############################## // ####################################################################################################### -#define CPLUGIN_011 -#define CPLUGIN_ID_011 11 -#define CPLUGIN_NAME_011 "Generic HTTP Advanced [TESTING]" +# define CPLUGIN_011 +# define CPLUGIN_ID_011 11 +# define CPLUGIN_NAME_011 "Generic HTTP Advanced [TESTING]" -#define C011_HTTP_METHOD_MAX_LEN 16 -#define C011_HTTP_URI_MAX_LEN 240 -#define C011_HTTP_HEADER_MAX_LEN 256 -#define C011_HTTP_BODY_MAX_LEN 512 +# define C011_HTTP_METHOD_MAX_LEN 16 +# define C011_HTTP_URI_MAX_LEN 240 +# define C011_HTTP_HEADER_MAX_LEN 256 +# define C011_HTTP_BODY_MAX_LEN 512 + + +bool C011_sendBinary = false; struct C011_ConfigStruct { @@ -54,48 +57,70 @@ bool CPlugin_011(CPlugin::Function function, struct EventStruct *event, String& } case CPlugin::Function::CPLUGIN_INIT: + { { - success = init_c011_delay_queue(event->ControllerIndex); - break; + MakeControllerSettings(ControllerSettings); + if (AllocatedControllerSettings()) { + LoadControllerSettings(event->ControllerIndex, ControllerSettings); + C011_sendBinary = ControllerSettings.sendBinary(); + } } + success = init_c011_delay_queue(event->ControllerIndex); + break; + } case CPlugin::Function::CPLUGIN_EXIT: - { - exit_c011_delay_queue(); - break; - } + { + exit_c011_delay_queue(); + break; + } case CPlugin::Function::CPLUGIN_WEBFORM_LOAD: { - String escapeBuffer; - std::shared_ptr customConfig(new C011_ConfigStruct); + { + String HttpMethod; + String HttpUri; + String HttpHeader; + String HttpBody; - if (customConfig) { - LoadCustomControllerSettings(event->ControllerIndex, (byte *)customConfig.get(), sizeof(C011_ConfigStruct)); - customConfig->zero_last(); + if (!load_C011_ConfigStruct(event->ControllerIndex, HttpMethod, HttpUri, HttpHeader, HttpBody)) + { + return false; + } + addTableSeparator(F("HTTP Config"), 2, 3); { byte choice = 0; String methods[] = { F("GET"), F("POST"), F("PUT"), F("HEAD"), F("PATCH") }; for (byte i = 0; i < 5; i++) { - if (methods[i].equals(customConfig->HttpMethod)) { + if (methods[i].equals(HttpMethod)) { choice = i; } } - addFormSelector(F("HTTP Method"), F("P011httpmethod"), 5, methods, NULL, choice); + addFormSelector(F("Method"), F("P011httpmethod"), 5, methods, NULL, choice); } - addFormTextBox(F("HTTP URI"), F("P011httpuri"), customConfig->HttpUri, C011_HTTP_URI_MAX_LEN - 1); + addFormTextBox(F("URI"), F("P011httpuri"), HttpUri, C011_HTTP_URI_MAX_LEN - 1); { - String escapeBuffer = customConfig->HttpHeader; - htmlEscape(escapeBuffer); - addFormTextArea(F("HTTP Header"), F("P011httpheader"), escapeBuffer, C011_HTTP_HEADER_MAX_LEN - 1, 4, 50); + htmlEscape(HttpHeader); + addFormTextArea(F("Header"), F("P011httpheader"), HttpHeader, C011_HTTP_HEADER_MAX_LEN - 1, 4, 50); } { - String escapeBuffer = customConfig->HttpBody; - htmlEscape(escapeBuffer); - addFormTextArea(F("HTTP Body"), F("P011httpbody"), escapeBuffer, C011_HTTP_BODY_MAX_LEN - 1, 8, 50); + htmlEscape(HttpBody); + addFormTextArea(F("Body"), F("P011httpbody"), HttpBody, C011_HTTP_BODY_MAX_LEN - 1, 8, 50); + } + } + { + + // Place in scope to delete ControllerSettings as soon as it is no longer needed + MakeControllerSettings(ControllerSettings); + if (!AllocatedControllerSettings()) { + addHtmlError(F("Out of memory, cannot load page")); + } else { + LoadControllerSettings(event->ControllerIndex, ControllerSettings); + addControllerParameterForm(ControllerSettings, event->ControllerIndex, ControllerSettingsStruct::CONTROLLER_SEND_BINARY); + addFormNote(F("Do not 'percent escape' body when send binary checked")); } } break; @@ -133,10 +158,6 @@ bool CPlugin_011(CPlugin::Function function, struct EventStruct *event, String& case CPlugin::Function::CPLUGIN_PROTOCOL_SEND: { - if (C011_DelayHandler == nullptr) { - break; - } - success = Create_schedule_HTTP_C011(event); break; } @@ -166,11 +187,41 @@ bool do_process_c011_delay_queue(int controller_number, const C011_queue_element bool do_process_c011_delay_queue(int controller_number, const C011_queue_element& element, ControllerSettingsStruct& ControllerSettings) { WiFiClient client; - if (!try_connect_host(controller_number, client, ControllerSettings)) { + if (!NetworkConnected()) { return false; } + + int httpCode = -1; + + send_via_http( + controller_number, + ControllerSettings, + element.controller_idx, + client, + element.uri, + element.HttpMethod, + element.header, + element.postStr, + httpCode); + + // HTTP codes: + // 1xx Informational response + // 2xx Success + return httpCode >= 100 && httpCode < 300; +} + +bool load_C011_ConfigStruct(controllerIndex_t ControllerIndex, String& HttpMethod, String& HttpUri, String& HttpHeader, String& HttpBody) { + // Just copy the needed strings and destruct the C011_ConfigStruct as soon as possible + std::shared_ptr customConfig(new C011_ConfigStruct); + + if (!customConfig) { return false; } - - return send_via_http(controller_number, client, element.txt, ControllerSettings.MustCheckReply); + LoadCustomControllerSettings(ControllerIndex, (byte *)customConfig.get(), sizeof(C011_ConfigStruct)); + customConfig->zero_last(); + HttpMethod = customConfig->HttpMethod; + HttpUri = customConfig->HttpUri; + HttpHeader = customConfig->HttpHeader; + HttpBody = customConfig->HttpBody; + return true; } // ******************************************************************************** @@ -179,57 +230,17 @@ bool do_process_c011_delay_queue(int controller_number, const C011_queue_element boolean Create_schedule_HTTP_C011(struct EventStruct *event) { if (C011_DelayHandler == nullptr) { + addLog(LOG_LEVEL_ERROR, F("No C011_DelayHandler")); return false; } - String authHeader; - String hostportString; - if (ExtraTaskSettings.TaskIndex != event->TaskIndex) { String dummy; PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummy); } - { - // Have the ControllerSettings in a separate scope so we can free it as soon as possible - MakeControllerSettings(ControllerSettings); - - if (!AllocatedControllerSettings()) { - return false; - } - LoadControllerSettings(event->ControllerIndex, ControllerSettings); - - authHeader = get_auth_header(event->ControllerIndex, ControllerSettings); - const bool defaultport = ControllerSettings.Port == 0 || ControllerSettings.Port == 80; - hostportString = defaultport ? ControllerSettings.getHost() : ControllerSettings.getHostPortString(); - } - - - std::shared_ptr customConfig(new C011_ConfigStruct); - - if (!customConfig) { - return false; - } - LoadCustomControllerSettings(event->ControllerIndex, (byte *)customConfig.get(), sizeof(C011_ConfigStruct)); - customConfig->zero_last(); - - bool success = false; - - { - String payload = do_create_http_request( - hostportString, - customConfig->HttpMethod, - customConfig->HttpUri, - authHeader, - "", - -1); - - // Remove extra newline, see https://github.com/letscontrolit/ESPEasy/issues/1970 - removeExtraNewLine(payload); - - // Add a new element to the queue with the minimal payload - success = C011_DelayHandler->addToQueue(C011_queue_element(event->ControllerIndex, payload)); - } + // Add a new element to the queue with the minimal payload + bool success = C011_DelayHandler->addToQueue(C011_queue_element(event)); if (success) { // Element was added. @@ -237,23 +248,29 @@ boolean Create_schedule_HTTP_C011(struct EventStruct *event) // and thus preventing the need to create a long string only to copy it to a queue element. C011_queue_element& element = C011_DelayHandler->sendQueue.back(); - if (strlen(customConfig->HttpHeader) > 0) { - element.txt += customConfig->HttpHeader; - removeExtraNewLine(element.txt); + if (!load_C011_ConfigStruct(event->ControllerIndex, element.HttpMethod, element.uri, element.header, element.postStr)) + { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + String log = F("C011 : "); + log += element.HttpMethod; + log += element.uri; + log += element.header; + log += element.postStr; + addLog(LOG_LEVEL_ERROR, log); + } + C011_DelayHandler->sendQueue.pop_back(); + return false; } - ReplaceTokenByValue(element.txt, event); - if (strlen(customConfig->HttpBody) > 0) + ReplaceTokenByValue(element.uri, event, false); + ReplaceTokenByValue(element.header, event, false); + + if (element.postStr.length() > 0) { - String body = String(customConfig->HttpBody); - ReplaceTokenByValue(body, event); - element.txt += F("Content-Length: "); - element.txt += String(body.length()); - addNewLine(element.txt); - addNewLine(element.txt); // Need 2 CRLF between header and body. - element.txt += body; + ReplaceTokenByValue(element.postStr, event, C011_sendBinary); } - addNewLine(element.txt); + } else { + addLog(LOG_LEVEL_ERROR, F("C011 : Could not add to delay handler")); } Scheduler.scheduleNextDelayQueue(ESPEasy_Scheduler::IntervalTimer_e::TIMER_C011_DELAY_QUEUE, C011_DelayHandler->getNextScheduleTime()); @@ -309,7 +326,7 @@ void DeleteNotNeededValues(String& s, byte numberOfValuesWanted) // in case of a sensor with 2 values: // SENSORVALUENAME1____TASKNAME1____VALUE1__SENSORVALUENAME2____TASKNAME2____VALUE2 // ******************************************************************************** -void ReplaceTokenByValue(String& s, struct EventStruct *event) +void ReplaceTokenByValue(String& s, struct EventStruct *event, bool sendBinary) { // example string: // write?db=testdb&type=%1%%vname1%%/1%%2%;%vname2%%/2%%3%;%vname3%%/3%%4%;%vname4%%/4%&value=%1%%val1%%/1%%2%;%val2%%/2%%3%;%val3%%/3%%4%;%val4%%/4% @@ -324,7 +341,7 @@ void ReplaceTokenByValue(String& s, struct EventStruct *event) addLog(LOG_LEVEL_DEBUG_MORE, F("HTTP after parsing: ")); addLog(LOG_LEVEL_DEBUG_MORE, s); - parseControllerVariables(s, event, true); + parseControllerVariables(s, event, !sendBinary); addLog(LOG_LEVEL_DEBUG_MORE, F("HTTP after replacements: ")); addLog(LOG_LEVEL_DEBUG_MORE, s); diff --git a/src/_C012.ino b/src/_C012.ino index 0672963539..808d1aee11 100644 --- a/src/_C012.ino +++ b/src/_C012.ino @@ -61,13 +61,6 @@ bool CPlugin_012(CPlugin::Function function, struct EventStruct *event, String& PluginCall(PLUGIN_GET_DEVICEVALUENAMES, event, dummy); } - MakeControllerSettings(ControllerSettings); - if (!AllocatedControllerSettings()) { - break; - } - - LoadControllerSettings(event->ControllerIndex, ControllerSettings); - for (byte x = 0; x < valueCount; x++) { bool isvalid; diff --git a/src/_C017.ino b/src/_C017.ino index c2f35e6beb..81c2dde9e9 100644 --- a/src/_C017.ino +++ b/src/_C017.ino @@ -65,12 +65,6 @@ bool CPlugin_017(CPlugin::Function function, struct EventStruct *event, String & byte valueCount = getValueCountFromSensorType(event->sensorType); C017_queue_element element(event); - MakeControllerSettings(ControllerSettings); - if (!AllocatedControllerSettings()) { - break; - } - LoadControllerSettings(event->ControllerIndex, ControllerSettings); - for (byte x = 0; x < valueCount; x++) { element.txt[x] = formatUserVarNoCheck(event, x); diff --git a/src/_C018.ino b/src/_C018.ino index 02e4cde761..9e928e1568 100644 --- a/src/_C018.ino +++ b/src/_C018.ino @@ -6,27 +6,24 @@ // ########################### Controller Plugin 018: LoRa TTN - RN2483/RN2903 ########################### // ####################################################################################################### -#define CPLUGIN_018 -#define CPLUGIN_ID_018 18 -#define CPLUGIN_NAME_018 "LoRa TTN - RN2483/RN2903 [TESTING]" +# define CPLUGIN_018 +# define CPLUGIN_ID_018 18 +# define CPLUGIN_NAME_018 "LoRa TTN - RN2483/RN2903 [TESTING]" +# define C018_BAUDRATE_LABEL "baudrate" -#define C018_BAUDRATE 9600 -#define C018_BAUDRATE_LABEL "baudrate" - - -#include -#include -#include "src/Globals/CPlugins.h" -#include "src/Globals/Protocol.h" -#include "src/ControllerQueue/C018_queue_element.h" -#include "ESPEasy_plugindefs.h" -#include "ESPEasy_fdwdecl.h" +# include +# include +# include "ESPEasy_fdwdecl.h" +# include "ESPEasy_plugindefs.h" +# include "src/ControllerQueue/C018_queue_element.h" +# include "src/Globals/CPlugins.h" +# include "src/Globals/Protocol.h" // Have this define after the includes, so we can set it in Custom.h -#ifndef C018_FORCE_SW_SERIAL -#define C018_FORCE_SW_SERIAL false -#endif +# ifndef C018_FORCE_SW_SERIAL +# define C018_FORCE_SW_SERIAL false +# endif // ifndef C018_FORCE_SW_SERIAL struct C018_data_struct { C018_data_struct() : C018_easySerial(nullptr), myLora(nullptr) {} @@ -102,6 +99,7 @@ struct C018_data_struct { bool useOTAA() const { if (!isInitialized()) { return true; } bool res = myLora->useOTAA(); + C018_logError(F("useOTA()")); return res; } @@ -130,6 +128,7 @@ struct C018_data_struct { bool setFrequencyPlan(RN2xx3_datatypes::Freq_plan plan) { if (!isInitialized()) { return false; } bool res = myLora->setFrequencyPlan(plan); + C018_logError(F("setFrequencyPlan()")); return res; } @@ -137,6 +136,7 @@ struct C018_data_struct { bool setSF(uint8_t sf) { if (!isInitialized()) { return false; } bool res = myLora->setSF(sf); + C018_logError(F("setSF()")); return res; } @@ -144,6 +144,7 @@ struct C018_data_struct { bool initOTAA(const String& AppEUI = "", const String& AppKey = "", const String& DevEUI = "") { if (myLora == nullptr) { return false; } bool success = myLora->initOTAA(AppEUI, AppKey, DevEUI); + C018_logError(F("initOTAA()")); updateCacheOnInit(); return success; @@ -152,6 +153,7 @@ struct C018_data_struct { bool initABP(const String& addr, const String& AppSKey, const String& NwkSKey) { if (myLora == nullptr) { return false; } bool success = myLora->initABP(addr, AppSKey, NwkSKey); + C018_logError(F("initABP()")); updateCacheOnInit(); return success; @@ -166,6 +168,7 @@ struct C018_data_struct { addLog(LOG_LEVEL_INFO, log); } String res = myLora->sendRawCommand(command); + C018_logError(F("sendRawCommand()")); return res; } @@ -188,6 +191,7 @@ struct C018_data_struct { String getDataRate() { if (!isInitialized()) { return ""; } String res = myLora->getDataRate(); + C018_logError(F("getDataRate()")); return res; } @@ -210,6 +214,7 @@ struct C018_data_struct { bool getFrameCounters(uint32_t& dnctr, uint32_t& upctr) { if (!isInitialized()) { return false; } bool res = myLora->getFrameCounters(dnctr, upctr); + C018_logError(F("getFrameCounters()")); return res; } @@ -217,6 +222,7 @@ struct C018_data_struct { bool setFrameCounters(uint32_t dnctr, uint32_t upctr) { if (!isInitialized()) { return false; } bool res = myLora->setFrameCounters(dnctr, upctr); + C018_logError(F("setFrameCounters()")); return res; } @@ -263,7 +269,20 @@ struct C018_data_struct { void async_loop() { if (isInitialized()) { - myLora->async_loop(); + rn2xx3_handler::RN_state state = myLora->async_loop(); + if (rn2xx3_handler::RN_state::must_perform_init == state) { + if (myLora->get_busy_count() > 10) { + if (_resetPin != -1) { + pinMode(_resetPin, OUTPUT); + digitalWrite(_resetPin, LOW); + delay(50); + digitalWrite(_resetPin, HIGH); + delay(200); + } + autobaud_success = false; +// triggerAutobaud(); + } + } } } @@ -286,22 +305,22 @@ private: void updateCacheOnInit() { cacheDevAddr = ""; - if (!isInitialized()) { - return; - } - - if (myLora->getStatus().Joined) - { - cacheDevAddr = myLora->sendRawCommand(F("mac get devaddr")); + if (isInitialized()) { + if (myLora->getStatus().Joined) + { + cacheDevAddr = myLora->sendRawCommand(F("mac get devaddr")); - if (cacheDevAddr == F("00000000")) { - cacheDevAddr = ""; + if (cacheDevAddr == F("00000000")) { + cacheDevAddr = ""; + } } } } void triggerAutobaud() { - if (C018_easySerial == nullptr || myLora == nullptr) return; + if ((C018_easySerial == nullptr) || (myLora == nullptr)) { + return; + } int retries = 2; while (retries > 0 && !autobaud_success) { @@ -348,8 +367,6 @@ private: } } - - ESPeasySerial *C018_easySerial = nullptr; rn2xx3 *myLora = nullptr; String cacheDevAddr; @@ -358,17 +375,17 @@ private: unsigned long _baudrate = 57600; uint8_t sampleSetCounter = 0; taskIndex_t sampleSetInitiator = INVALID_TASK_INDEX; - int8_t _resetPin = -1; + int8_t _resetPin = -1; bool autobaud_success = false; } C018_data; -#define C018_DEVICE_EUI_LEN 17 -#define C018_DEVICE_ADDR_LEN 33 -#define C018_NETWORK_SESSION_KEY_LEN 33 -#define C018_APP_SESSION_KEY_LEN 33 -#define C018_USE_OTAA 0 -#define C018_USE_ABP 1 +# define C018_DEVICE_EUI_LEN 17 +# define C018_DEVICE_ADDR_LEN 33 +# define C018_NETWORK_SESSION_KEY_LEN 33 +# define C018_APP_SESSION_KEY_LEN 33 +# define C018_USE_OTAA 0 +# define C018_USE_ABP 1 struct C018_ConfigStruct { C018_ConfigStruct() { @@ -432,7 +449,7 @@ bool CPlugin_018(CPlugin::Function function, struct EventStruct *event, String& Protocol[protocolCount].usesCheckReply = false; Protocol[protocolCount].usesTimeout = false; Protocol[protocolCount].usesSampleSets = true; - Protocol[protocolCount].needsNetwork = false; + Protocol[protocolCount].needsNetwork = false; break; } @@ -457,6 +474,7 @@ bool CPlugin_018(CPlugin::Function function, struct EventStruct *event, String& case CPlugin::Function::CPLUGIN_INIT: { success = init_c018_delay_queue(event->ControllerIndex); + if (success) { C018_init(event); } @@ -472,11 +490,7 @@ bool CPlugin_018(CPlugin::Function function, struct EventStruct *event, String& case CPlugin::Function::CPLUGIN_WEBFORM_LOAD: { - C018_ConfigStruct customConfig; - - LoadCustomControllerSettings(event->ControllerIndex, (byte *)&customConfig, sizeof(customConfig)); - customConfig.validate(); - + addTableSeparator(F("Credentials"), 2, 3); { // Script to toggle visibility of OTAA/ABP field, based on the activation method selector. protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(event->ControllerIndex); @@ -494,51 +508,79 @@ bool CPlugin_018(CPlugin::Function function, struct EventStruct *event, String& html_add_script_end(); } + unsigned long baudrate; + int8_t rxpin; + int8_t txpin; + int8_t resetpin; + uint8_t sf; + uint8_t frequencyplan; + uint8_t joinmethod; + { - addFormTextBox(F("Device EUI"), F("deveui"), customConfig.DeviceEUI, C018_DEVICE_EUI_LEN - 1); - String deveui_note = F("Leave empty to use HW DevEUI: "); - deveui_note += C018_data.hweui(); - addFormNote(deveui_note, F("deveui_note")); - } + // Keep this object in a small scope so we can destruct it as soon as possible again. + std::shared_ptr customConfig(new C018_ConfigStruct); + + if (!customConfig) { + break; + } + LoadCustomControllerSettings(event->ControllerIndex, (byte *)customConfig.get(), sizeof(C018_ConfigStruct)); + customConfig->validate(); + baudrate = customConfig->baudrate; + rxpin = customConfig->rxpin; + txpin = customConfig->txpin; + resetpin = customConfig->resetpin; + sf = customConfig->sf; + frequencyplan = customConfig->frequencyplan; + joinmethod = customConfig->joinmethod; + + { + addFormTextBox(F("Device EUI"), F("deveui"), customConfig->DeviceEUI, C018_DEVICE_EUI_LEN - 1); + String deveui_note = F("Leave empty to use HW DevEUI: "); + deveui_note += C018_data.hweui(); + addFormNote(deveui_note, F("deveui_note")); + } - addFormTextBox(F("Device Addr"), F("devaddr"), customConfig.DeviceAddr, C018_DEVICE_ADDR_LEN - 1); - addFormTextBox(F("Network Session Key"), F("nskey"), customConfig.NetworkSessionKey, C018_NETWORK_SESSION_KEY_LEN - 1); - addFormTextBox(F("App Session Key"), F("appskey"), customConfig.AppSessionKey, C018_APP_SESSION_KEY_LEN - 1); + addFormTextBox(F("Device Addr"), F("devaddr"), customConfig->DeviceAddr, C018_DEVICE_ADDR_LEN - 1); + addFormTextBox(F("Network Session Key"), F("nskey"), customConfig->NetworkSessionKey, C018_NETWORK_SESSION_KEY_LEN - 1); + addFormTextBox(F("App Session Key"), F("appskey"), customConfig->AppSessionKey, C018_APP_SESSION_KEY_LEN - 1); + } { - byte choice = customConfig.joinmethod; String options[2] = { F("OTAA"), F("ABP") }; int values[2] = { C018_USE_OTAA, C018_USE_ABP }; addFormSelector_script(F("Activation Method"), F("joinmethod"), 2, - options, values, NULL, choice, + options, values, NULL, joinmethod, F("joinChanged(this)")); // Script to toggle OTAA/ABP fields visibility when changing selection. } html_add_script(F("document.getElementById('joinmethod').onchange();"), false); addTableSeparator(F("Connection Configuration"), 2, 3); { - byte choice = customConfig.frequencyplan; String options[4] = { F("SINGLE_CHANNEL_EU"), F("TTN_EU"), F("TTN_US"), F("DEFAULT_EU") }; - int values[4] = - { RN2xx3_datatypes::Freq_plan::SINGLE_CHANNEL_EU, RN2xx3_datatypes::Freq_plan::TTN_EU, RN2xx3_datatypes::Freq_plan::TTN_US, - RN2xx3_datatypes::Freq_plan::DEFAULT_EU }; - - addFormSelector(F("Frequency Plan"), F("frequencyplan"), 4, options, values, NULL, choice, false); + int values[4] = + { + RN2xx3_datatypes::Freq_plan::SINGLE_CHANNEL_EU, + RN2xx3_datatypes::Freq_plan::TTN_EU, + RN2xx3_datatypes::Freq_plan::TTN_US, + RN2xx3_datatypes::Freq_plan::DEFAULT_EU + }; + + addFormSelector(F("Frequency Plan"), F("frequencyplan"), 4, options, values, NULL, frequencyplan, false); } - addFormNumericBox(F("Spread Factor"), F("sf"), customConfig.sf, 7, 12); + addFormNumericBox(F("Spread Factor"), F("sf"), sf, 7, 12); addTableSeparator(F("Serial Port Configuration"), 2, 3); // Optional reset pin RN2xx3 - addFormPinSelect(formatGpioName_output_optional(F("Reset")), F("taskdevicepin3"), customConfig.resetpin); + addFormPinSelect(formatGpioName_output_optional(F("Reset")), F("taskdevicepin3"), resetpin); // Show serial port selection - addFormPinSelect(formatGpioName_RX(false), F("taskdevicepin1"), customConfig.rxpin); - addFormPinSelect(formatGpioName_TX(false), F("taskdevicepin2"), customConfig.txpin); - serialHelper_webformLoad(customConfig.rxpin, customConfig.txpin, true); + addFormPinSelect(formatGpioName_RX(false), F("taskdevicepin1"), rxpin); + addFormPinSelect(formatGpioName_TX(false), F("taskdevicepin2"), txpin); + serialHelper_webformLoad(rxpin, txpin, true); - addFormNumericBox(F("Baudrate"), F(C018_BAUDRATE_LABEL), customConfig.baudrate, 2400, 115200); + addFormNumericBox(F("Baudrate"), F(C018_BAUDRATE_LABEL), baudrate, 2400, 115200); addUnit(F("baud")); addFormNote(F("Module default baudrate: 57600 bps")); @@ -590,26 +632,29 @@ bool CPlugin_018(CPlugin::Function function, struct EventStruct *event, String& } case CPlugin::Function::CPLUGIN_WEBFORM_SAVE: { - C018_ConfigStruct customConfig; - customConfig.reset(); - String deveui = web_server.arg(F("deveui")); - String devaddr = web_server.arg(F("devaddr")); - String nskey = web_server.arg(F("nskey")); - String appskey = web_server.arg(F("appskey")); - - strlcpy(customConfig.DeviceEUI, deveui.c_str(), sizeof(customConfig.DeviceEUI)); - strlcpy(customConfig.DeviceAddr, devaddr.c_str(), sizeof(customConfig.DeviceAddr)); - strlcpy(customConfig.NetworkSessionKey, nskey.c_str(), sizeof(customConfig.NetworkSessionKey)); - strlcpy(customConfig.AppSessionKey, appskey.c_str(), sizeof(customConfig.AppSessionKey)); - customConfig.baudrate = getFormItemInt(F(C018_BAUDRATE_LABEL), customConfig.baudrate); - customConfig.rxpin = getFormItemInt(F("taskdevicepin1"), customConfig.rxpin); - customConfig.txpin = getFormItemInt(F("taskdevicepin2"), customConfig.txpin); - customConfig.resetpin = getFormItemInt(F("taskdevicepin3"), customConfig.resetpin); - customConfig.sf = getFormItemInt(F("sf"), customConfig.sf); - customConfig.frequencyplan = getFormItemInt(F("frequencyplan"), customConfig.frequencyplan); - customConfig.joinmethod = getFormItemInt(F("joinmethod"), customConfig.joinmethod); - serialHelper_webformSave(customConfig.rxpin, customConfig.txpin); - SaveCustomControllerSettings(event->ControllerIndex, (byte *)&customConfig, sizeof(customConfig)); + std::shared_ptr customConfig(new C018_ConfigStruct); + + if (customConfig) { + customConfig->reset(); + String deveui = web_server.arg(F("deveui")); + String devaddr = web_server.arg(F("devaddr")); + String nskey = web_server.arg(F("nskey")); + String appskey = web_server.arg(F("appskey")); + + strlcpy(customConfig->DeviceEUI, deveui.c_str(), sizeof(customConfig->DeviceEUI)); + strlcpy(customConfig->DeviceAddr, devaddr.c_str(), sizeof(customConfig->DeviceAddr)); + strlcpy(customConfig->NetworkSessionKey, nskey.c_str(), sizeof(customConfig->NetworkSessionKey)); + strlcpy(customConfig->AppSessionKey, appskey.c_str(), sizeof(customConfig->AppSessionKey)); + customConfig->baudrate = getFormItemInt(F(C018_BAUDRATE_LABEL), customConfig->baudrate); + customConfig->rxpin = getFormItemInt(F("taskdevicepin1"), customConfig->rxpin); + customConfig->txpin = getFormItemInt(F("taskdevicepin2"), customConfig->txpin); + customConfig->resetpin = getFormItemInt(F("taskdevicepin3"), customConfig->resetpin); + customConfig->sf = getFormItemInt(F("sf"), customConfig->sf); + customConfig->frequencyplan = getFormItemInt(F("frequencyplan"), customConfig->frequencyplan); + customConfig->joinmethod = getFormItemInt(F("joinmethod"), customConfig->joinmethod); + serialHelper_webformSave(customConfig->rxpin, customConfig->txpin); + SaveCustomControllerSettings(event->ControllerIndex, (byte *)customConfig.get(), sizeof(C018_ConfigStruct)); + } break; } @@ -646,6 +691,7 @@ bool CPlugin_018(CPlugin::Function function, struct EventStruct *event, String& success = C018_DelayHandler->addToQueue( C018_queue_element(event, C018_data.getSampleSetCount(event->TaskIndex))); Scheduler.scheduleNextDelayQueue(ESPEasy_Scheduler::IntervalTimer_e::TIMER_C018_DELAY_QUEUE, C018_DelayHandler->getNextScheduleTime()); + if (!C018_data.isInitialized()) { // Sometimes the module does need some time after power on to respond. // So it may not be initialized well at the call of CPLUGIN_INIT @@ -685,51 +731,68 @@ bool CPlugin_018(CPlugin::Function function, struct EventStruct *event, String& } bool C018_init(struct EventStruct *event) { - MakeControllerSettings(ControllerSettings); - if (!AllocatedControllerSettings()) { - return false; + String AppEUI; + String AppKey; + taskIndex_t SampleSetInitiator = INVALID_TASK_INDEX; + unsigned int Port = 0; + { + // Allocate ControllerSettings object in a scope, so we can destruct it as soon as possible. + MakeControllerSettings(ControllerSettings); + + if (!AllocatedControllerSettings()) { + return false; + } + + LoadControllerSettings(event->ControllerIndex, ControllerSettings); + C018_DelayHandler->configureControllerSettings(ControllerSettings); + AppEUI = getControllerUser(event->ControllerIndex, ControllerSettings); + AppKey = getControllerPass(event->ControllerIndex, ControllerSettings); + SampleSetInitiator = ControllerSettings.SampleSetInitiator; + Port = ControllerSettings.Port; } - LoadControllerSettings(event->ControllerIndex, ControllerSettings); - C018_DelayHandler->configureControllerSettings(ControllerSettings); + std::shared_ptr customConfig(new C018_ConfigStruct); - C018_ConfigStruct customConfig; - LoadCustomControllerSettings(event->ControllerIndex, (byte *)&customConfig, sizeof(customConfig)); - customConfig.validate(); + if (!customConfig) { + return false; + } + LoadCustomControllerSettings(event->ControllerIndex, (byte *)customConfig.get(), sizeof(C018_ConfigStruct)); + customConfig->validate(); - if (!C018_data.init(customConfig.rxpin, customConfig.txpin, customConfig.baudrate, - customConfig.joinmethod == C018_USE_OTAA, - ControllerSettings.SampleSetInitiator, customConfig.resetpin)) + if (!C018_data.init(customConfig->rxpin, customConfig->txpin, customConfig->baudrate, + (customConfig->joinmethod == C018_USE_OTAA), + SampleSetInitiator, customConfig->resetpin)) { return false; } - C018_data.setFrequencyPlan(static_cast(customConfig.frequencyplan)); + C018_data.setFrequencyPlan(static_cast(customConfig->frequencyplan)); - if (customConfig.joinmethod == C018_USE_OTAA) { - String AppEUI = getControllerUser(event->ControllerIndex, ControllerSettings); - String AppKey = getControllerPass(event->ControllerIndex, ControllerSettings); + if (customConfig->joinmethod == C018_USE_OTAA) { String log = F("OTAA: AppEUI: "); log += AppEUI; log += F(" AppKey: "); log += AppKey; log += F(" DevEUI: "); - log += customConfig.DeviceEUI; + log += customConfig->DeviceEUI; addLog(LOG_LEVEL_INFO, log); - if (!C018_data.initOTAA(AppEUI, AppKey, customConfig.DeviceEUI)) { + + if (!C018_data.initOTAA(AppEUI, AppKey, customConfig->DeviceEUI)) { return false; } } else { - if (!C018_data.initABP(customConfig.DeviceAddr, customConfig.AppSessionKey, customConfig.NetworkSessionKey)) { + if (!C018_data.initABP(customConfig->DeviceAddr, customConfig->AppSessionKey, customConfig->NetworkSessionKey)) { return false; } } - if (!C018_data.setSF(customConfig.sf)) { + + if (!C018_data.setSF(customConfig->sf)) { return false; } - if (!C018_data.txUncnf("ESPeasy (TTN)", ControllerSettings.Port)) { + + if (!C018_data.txUncnf("ESPeasy (TTN)", Port)) { return false; } return true; diff --git a/src/_CPlugin_Helper.cpp b/src/_CPlugin_Helper.cpp index 69069b4a0b..502f8d7dd9 100644 --- a/src/_CPlugin_Helper.cpp +++ b/src/_CPlugin_Helper.cpp @@ -23,6 +23,14 @@ #include #include + +#ifdef ESP8266 +# include +#endif // ifdef ESP8266 +#ifdef ESP32 +# include "HTTPClient.h" +#endif // ifdef ESP32 + bool safeReadStringUntil(Stream & input, String & str, char terminator, @@ -103,18 +111,27 @@ String get_auth_header(int controller_index, const ControllerSettingsStruct& Con return authHeader; } +String get_user_agent_string() { + static unsigned int agent_size = 20; + String userAgent; + userAgent.reserve(agent_size); + userAgent += F("ESP Easy/"); + userAgent += BUILD; + userAgent += '/'; + userAgent += get_build_date(); + userAgent += ' '; + userAgent += get_build_time(); + agent_size = userAgent.length(); + return userAgent; +} + String get_user_agent_request_header_field() { static unsigned int agent_size = 20; String request; request.reserve(agent_size); request = F("User-Agent: "); - request += F("ESP Easy/"); - request += BUILD; - request += '/'; - request += get_build_date(); - request += ' '; - request += get_build_time(); + request += get_user_agent_string(); request += "\r\n"; agent_size = request.length(); return request; @@ -132,6 +149,7 @@ String do_create_http_request( if (content_length >= 0) { estimated_size += 45; } String request; + request.reserve(estimated_size); request += method; request += ' '; @@ -229,7 +247,7 @@ void log_connecting_to(const String& prefix, int controller_number, ControllerSe #endif // ifndef BUILD_NO_DEBUG -void log_connecting_fail(const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings) { +void log_connecting_fail(const String& prefix, int controller_number) { if (loglevelActiveFor(LOG_LEVEL_ERROR)) { String log = prefix; log += get_formatted_Controller_number(controller_number); @@ -242,11 +260,11 @@ void log_connecting_fail(const String& prefix, int controller_number, Controller } } -bool count_connection_results(bool success, const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings) { +bool count_connection_results(bool success, const String& prefix, int controller_number) { if (!success) { ++connectionFailures; - log_connecting_fail(prefix, controller_number, ControllerSettings); + log_connecting_fail(prefix, controller_number); return false; } statusLED(true); @@ -262,20 +280,21 @@ bool try_connect_host(int controller_number, WiFiUDP& client, ControllerSettings if (!NetworkConnected()) { return false; } client.setTimeout(ControllerSettings.ClientTimeout); + yield(); #ifndef BUILD_NO_DEBUG log_connecting_to(F("UDP : "), controller_number, ControllerSettings); #endif // ifndef BUILD_NO_DEBUG bool success = ControllerSettings.beginPacket(client); const bool result = count_connection_results( success, - F("UDP : "), controller_number, ControllerSettings); + F("UDP : "), controller_number); STOP_TIMER(TRY_CONNECT_HOST_UDP); return result; } bool try_connect_host(int controller_number, WiFiClient& client, ControllerSettingsStruct& ControllerSettings) { - return try_connect_host(controller_number, client, ControllerSettings, F("HTTP : ")); -} + return try_connect_host(controller_number, client, ControllerSettings, F("HTTP : ")); +} bool try_connect_host(int controller_number, WiFiClient& client, ControllerSettingsStruct& ControllerSettings, const String& loglabel) { START_TIMER; @@ -283,6 +302,7 @@ bool try_connect_host(int controller_number, WiFiClient& client, ControllerSetti if (!NetworkConnected()) { return false; } // Use WiFiClient class to create TCP connections + yield(); client.setTimeout(ControllerSettings.ClientTimeout); #ifndef BUILD_NO_DEBUG log_connecting_to(loglabel, controller_number, ControllerSettings); @@ -290,7 +310,7 @@ bool try_connect_host(int controller_number, WiFiClient& client, ControllerSetti const bool success = ControllerSettings.connectToHost(client); const bool result = count_connection_results( success, - loglabel, controller_number, ControllerSettings); + loglabel, controller_number); STOP_TIMER(TRY_CONNECT_HOST_TCP); return result; } @@ -415,10 +435,156 @@ bool send_via_http(int controller_number, WiFiClient& client, const String& post return send_via_http(get_formatted_Controller_number(controller_number), client, postStr, must_check_reply); } +String send_via_http(int controller_number, + const ControllerSettingsStruct& ControllerSettings, + controllerIndex_t controller_idx, + WiFiClient & client, + const String & uri, + const String & HttpMethod, + const String & header, + const String & postStr, + int & httpCode) { + client.setTimeout(ControllerSettings.ClientTimeout); + const String result = send_via_http( + get_formatted_Controller_number(controller_number), + client, + ControllerSettings.ClientTimeout, + getControllerUser(controller_idx, ControllerSettings), + getControllerPass(controller_idx, ControllerSettings), + ControllerSettings.getHost(), + ControllerSettings.Port, + uri, + HttpMethod, + header, + postStr, + httpCode); + + const bool success = httpCode > 0; + + count_connection_results( + success, + F("HTTP : "), + controller_number); + + return result; +} + +bool splitHeaders(int& strpos, const String& multiHeaders, String& name, String& value) { + if (strpos < 0) { + return false; + } + int colonPos = multiHeaders.indexOf(':', strpos); + + if (colonPos < 0) { + return false; + } + name = multiHeaders.substring(strpos, colonPos); + int valueEndPos = multiHeaders.indexOf('\n', colonPos + 1); + if (valueEndPos < 0) { + value = multiHeaders.substring(colonPos + 1); + strpos = -1; + } else { + value = multiHeaders.substring(colonPos + 1, valueEndPos); + strpos = valueEndPos + 1; + } + value.replace('\r', ' '); + value.trim(); + return true; +} + +String send_via_http(const String& logIdentifier, + WiFiClient & client, + uint16_t timeout, + const String& user, + const String& pass, + const String& host, + uint16_t port, + const String& uri, + const String& HttpMethod, + const String& header, + const String& postStr, + int & httpCode) { + HTTPClient http; + + http.setAuthorization(user.c_str(), pass.c_str()); + http.setTimeout(timeout); + http.setUserAgent(get_user_agent_string()); + + // Add request header as fall back. + // When adding another "accept" header, it may be interpreted as: + // "if you have XXX, send it; or failing that, just give me what you've got." + http.addHeader(F("Accept"), F("*/*;q=0.1")); + + yield(); +#if defined(CORE_POST_2_6_0) || defined(ESP32) + http.begin(client, host, port, uri, false); // HTTP +#else + http.begin(host, port, uri); +#endif + + { + int headerpos = 0; + String name, value; + while (splitHeaders(headerpos, header, name, value)) { + http.addHeader(name, value); + } + } + + // start connection and send HTTP header (and body) + if (HttpMethod.equals(F("HEAD")) || HttpMethod.equals(F("GET"))) { + httpCode = http.sendRequest(HttpMethod.c_str()); + } else { + httpCode = http.sendRequest(HttpMethod.c_str(), postStr); + } + + String response; + + // httpCode will be negative on error + if (httpCode > 0) { + response = http.getString(); + + byte loglevel = LOG_LEVEL_ERROR; + // HTTP codes: + // 1xx Informational response + // 2xx Success + if (httpCode >= 100 && httpCode < 300) { + loglevel = LOG_LEVEL_INFO; + } + + + if (loglevelActiveFor(loglevel)) { + String log = F("HTTP : "); + log += logIdentifier; + log += ' '; + log += HttpMethod; + log += F("... HTTP code: "); + log += String(httpCode); + + if (response.length() > 0) { + log += ' '; + log += response; + } + addLog(loglevel, log); + } + } else { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + String log = F("HTTP : "); + log += logIdentifier; + log += ' '; + log += HttpMethod; + log += F("... failed, error: "); + log += http.errorToString(httpCode); + addLog(LOG_LEVEL_ERROR, log); + } + } + http.end(); + return response; +} String getControllerUser(controllerIndex_t controller_idx, const ControllerSettingsStruct& ControllerSettings) { - if (!validControllerIndex(controller_idx)) return ""; + if (!validControllerIndex(controller_idx)) { return ""; } + if (ControllerSettings.useExtendedCredentials()) { return ExtendedControllerCredentials.getControllerUser(controller_idx); } @@ -427,7 +593,8 @@ String getControllerUser(controllerIndex_t controller_idx, const ControllerSetti String getControllerPass(controllerIndex_t controller_idx, const ControllerSettingsStruct& ControllerSettings) { - if (!validControllerIndex(controller_idx)) return ""; + if (!validControllerIndex(controller_idx)) { return ""; } + if (ControllerSettings.useExtendedCredentials()) { return ExtendedControllerCredentials.getControllerPass(controller_idx); } @@ -436,7 +603,8 @@ String getControllerPass(controllerIndex_t controller_idx, const ControllerSetti void setControllerUser(controllerIndex_t controller_idx, const ControllerSettingsStruct& ControllerSettings, const String& value) { - if (!validControllerIndex(controller_idx)) return; + if (!validControllerIndex(controller_idx)) { return; } + if (ControllerSettings.useExtendedCredentials()) { ExtendedControllerCredentials.setControllerUser(controller_idx, value); } else { @@ -446,7 +614,8 @@ void setControllerUser(controllerIndex_t controller_idx, const ControllerSetting void setControllerPass(controllerIndex_t controller_idx, const ControllerSettingsStruct& ControllerSettings, const String& value) { - if (!validControllerIndex(controller_idx)) return; + if (!validControllerIndex(controller_idx)) { return; } + if (ControllerSettings.useExtendedCredentials()) { ExtendedControllerCredentials.setControllerPass(controller_idx, value); } else { @@ -458,4 +627,4 @@ bool hasControllerCredentialsSet(controllerIndex_t controller_idx, const Control { return getControllerUser(controller_idx, ControllerSettings).length() != 0 && getControllerPass(controller_idx, ControllerSettings).length() != 0; -} \ No newline at end of file +} diff --git a/src/_CPlugin_Helper.h b/src/_CPlugin_Helper.h index 5f29716dbf..6905aadaa9 100644 --- a/src/_CPlugin_Helper.h +++ b/src/_CPlugin_Helper.h @@ -57,9 +57,9 @@ String create_http_request_auth(int controller_number, int controller_index, Con void log_connecting_to(const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings); #endif // ifndef BUILD_NO_DEBUG -void log_connecting_fail(const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings); +void log_connecting_fail(const String& prefix, int controller_number); -bool count_connection_results(bool success, const String& prefix, int controller_number, ControllerSettingsStruct& ControllerSettings); +bool count_connection_results(bool success, const String& prefix, int controller_number); bool try_connect_host(int controller_number, WiFiUDP& client, ControllerSettingsStruct& ControllerSettings); @@ -72,9 +72,39 @@ bool try_connect_host(int controller_number, WiFiClient& client, ControllerSetti // https://github.com/esp8266/Arduino/pull/1829 bool client_available(WiFiClient& client); -bool send_via_http(const String& logIdentifier, WiFiClient& client, const String& postStr, bool must_check_reply); - -bool send_via_http(int controller_number, WiFiClient& client, const String& postStr, bool must_check_reply); +bool send_via_http(const String& logIdentifier, + WiFiClient & client, + const String& postStr, + bool must_check_reply); + +bool send_via_http(int controller_number, + WiFiClient & client, + const String& postStr, + bool must_check_reply); + +String send_via_http(const String& logIdentifier, + WiFiClient & client, + uint16_t timeout, + const String& user, + const String& pass, + const String& host, + uint16_t port, + const String& uri, + const String& HttpMethod, + const String& header, + const String& postStr, + int & httpCode); + +String send_via_http(int controller_number, + const ControllerSettingsStruct& ControllerSettings, + controllerIndex_t controller_idx, + WiFiClient & client, + const String & uri, + const String & HttpMethod, + const String & header, + const String & postStr, + int & httpCode); + String getControllerUser(controllerIndex_t controller_idx, const ControllerSettingsStruct& ControllerSettings); String getControllerPass(controllerIndex_t controller_idx, const ControllerSettingsStruct& ControllerSettings); diff --git a/src/_CPlugin_Helper_webform.ino b/src/_CPlugin_Helper_webform.ino index 569a4b3bee..2a943f9e6e 100644 --- a/src/_CPlugin_Helper_webform.ino +++ b/src/_CPlugin_Helper_webform.ino @@ -7,7 +7,10 @@ /*********************************************************************************************\ * Functions to load and store controller settings on the web page. \*********************************************************************************************/ -String getControllerParameterName(protocolIndex_t ProtocolIndex, ControllerSettingsStruct::VarType parameterIdx, bool displayName, bool& isAlternative) { +String getControllerParameterName(protocolIndex_t ProtocolIndex, + ControllerSettingsStruct::VarType parameterIdx, + bool displayName, + bool & isAlternative) { String name; if (displayName) { @@ -48,6 +51,7 @@ String getControllerParameterName(protocolIndex_t ProtocolIndex, ControllerSetti case ControllerSettingsStruct::CONTROLLER_WILL_RETAIN: name = F("Will Retain"); break; case ControllerSettingsStruct::CONTROLLER_CLEAN_SESSION: name = F("Clean Session"); break; case ControllerSettingsStruct::CONTROLLER_USE_EXTENDED_CREDENTIALS: name = F("Use Extended Credentials"); break; + case ControllerSettingsStruct::CONTROLLER_SEND_BINARY: name = F("Send Binary"); break; case ControllerSettingsStruct::CONTROLLER_TIMEOUT: name = F("Client Timeout"); break; case ControllerSettingsStruct::CONTROLLER_SAMPLE_SET_INITIATOR: name = F("Sample Set Initiator"); break; @@ -82,6 +86,21 @@ String getControllerParameterDisplayName(protocolIndex_t ProtocolIndex, Controll return getControllerParameterName(ProtocolIndex, parameterIdx, displayName, isAlternative); } +void addControllerEnabledForm(controllerIndex_t controllerindex) { + protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(controllerindex); + + if (!validProtocolIndex(ProtocolIndex)) { + return; + } + + ControllerSettingsStruct::VarType varType = ControllerSettingsStruct::CONTROLLER_ENABLED; + + bool isAlternativeDisplayName = false; + String displayName = getControllerParameterDisplayName(ProtocolIndex, varType, isAlternativeDisplayName); + String internalName = getControllerParameterInternalName(ProtocolIndex, varType); + addFormCheckBox(displayName, internalName, Settings.ControllerEnabled[controllerindex]); +} + void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettings, controllerIndex_t controllerindex, ControllerSettingsStruct::VarType varType) { protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(controllerindex); if (!validProtocolIndex(ProtocolIndex)) { @@ -119,7 +138,8 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin } case ControllerSettingsStruct::CONTROLLER_USER: { - size_t fieldMaxLength = ControllerSettings.useExtendedCredentials() ? EXT_SECURITY_MAX_USER_LENGTH : sizeof(SecuritySettings.ControllerUser[0]) - 1; + size_t fieldMaxLength = + ControllerSettings.useExtendedCredentials() ? EXT_SECURITY_MAX_USER_LENGTH : sizeof(SecuritySettings.ControllerUser[0]) - 1; addFormTextBox(displayName, internalName, getControllerUser(controllerindex, ControllerSettings), @@ -138,7 +158,6 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin addFormPasswordBox(displayName, internalName, getControllerPass(controllerindex, ControllerSettings), fieldMaxLength); - } break; } @@ -175,7 +194,7 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin break; } case ControllerSettingsStruct::CONTROLLER_CLIENT_ID: - addFormTextBox(displayName, internalName, ControllerSettings.ClientID, sizeof(ControllerSettings.ClientID) - 1); + addFormTextBox(displayName, internalName, ControllerSettings.ClientID, sizeof(ControllerSettings.ClientID) - 1); break; case ControllerSettingsStruct::CONTROLLER_UNIQUE_CLIENT_ID_RECONNECT: addFormCheckBox(displayName, internalName, ControllerSettings.mqtt_uniqueMQTTclientIdReconnect()); @@ -210,6 +229,9 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin case ControllerSettingsStruct::CONTROLLER_USE_EXTENDED_CREDENTIALS: addFormCheckBox(displayName, internalName, ControllerSettings.useExtendedCredentials()); break; + case ControllerSettingsStruct::CONTROLLER_SEND_BINARY: + addFormCheckBox(displayName, internalName, ControllerSettings.sendBinary()); + break; case ControllerSettingsStruct::CONTROLLER_TIMEOUT: addFormNumericBox(displayName, internalName, ControllerSettings.ClientTimeout, 10, CONTROLLER_CLIENTTIMEOUT_MAX); addUnit(F("ms")); @@ -223,12 +245,15 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin } } -void saveControllerParameterForm(ControllerSettingsStruct& ControllerSettings, controllerIndex_t controllerindex, ControllerSettingsStruct::VarType varType) { +void saveControllerParameterForm(ControllerSettingsStruct & ControllerSettings, + controllerIndex_t controllerindex, + ControllerSettingsStruct::VarType varType) { protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(controllerindex); + if (!validProtocolIndex(ProtocolIndex)) { return; } - String internalName = getControllerParameterInternalName(ProtocolIndex, varType); + String internalName = getControllerParameterInternalName(ProtocolIndex, varType); switch (varType) { case ControllerSettingsStruct::CONTROLLER_USE_DNS: ControllerSettings.UseDNS = getFormItemInt(internalName); break; @@ -260,13 +285,14 @@ void saveControllerParameterForm(ControllerSettingsStruct& ControllerSettings, c setControllerUser(controllerindex, ControllerSettings, web_server.arg(internalName)); break; case ControllerSettingsStruct::CONTROLLER_PASS: - { - String password; - if (getFormPassword(internalName, password)) { - setControllerPass(controllerindex, ControllerSettings, password); - } + { + String password; + + if (getFormPassword(internalName, password)) { + setControllerPass(controllerindex, ControllerSettings, password); } break; + } case ControllerSettingsStruct::CONTROLLER_MIN_SEND_INTERVAL: ControllerSettings.MinimalTimeBetweenMessages = getFormItemInt(internalName, ControllerSettings.MinimalTimeBetweenMessages); @@ -285,7 +311,7 @@ void saveControllerParameterForm(ControllerSettingsStruct& ControllerSettings, c break; case ControllerSettingsStruct::CONTROLLER_CLIENT_ID: - strncpy_webserver_arg(ControllerSettings.ClientID, internalName); + strncpy_webserver_arg(ControllerSettings.ClientID, internalName); break; case ControllerSettingsStruct::CONTROLLER_UNIQUE_CLIENT_ID_RECONNECT: ControllerSettings.mqtt_uniqueMQTTclientIdReconnect(isFormItemChecked(internalName)); @@ -320,6 +346,9 @@ void saveControllerParameterForm(ControllerSettingsStruct& ControllerSettings, c case ControllerSettingsStruct::CONTROLLER_USE_EXTENDED_CREDENTIALS: ControllerSettings.useExtendedCredentials(isFormItemChecked(internalName)); break; + case ControllerSettingsStruct::CONTROLLER_SEND_BINARY: + ControllerSettings.sendBinary(isFormItemChecked(internalName)); + break; case ControllerSettingsStruct::CONTROLLER_TIMEOUT: ControllerSettings.ClientTimeout = getFormItemInt(internalName, ControllerSettings.ClientTimeout); break; diff --git a/src/_P037_MQTTImport.ino b/src/_P037_MQTTImport.ino index ecc156998d..b6e7515f6d 100644 --- a/src/_P037_MQTTImport.ino +++ b/src/_P037_MQTTImport.ino @@ -420,33 +420,41 @@ boolean MQTTConnect_037() Plugin_037_update_connect_status(); return false; // Not connected, so no use in wasting time to connect to a host. } - MakeControllerSettings(ControllerSettings); - if (!AllocatedControllerSettings()) { - addLog(LOG_LEVEL_ERROR, F("IMPT : Cannot load controller settings, out of RAM")); - return false; - } + + String user, pass; + bool hasCredentials = false; - LoadControllerSettings(enabledMqttController, ControllerSettings); - if (ControllerSettings.UseDNS) { - MQTTclient_037->setServer(ControllerSettings.getHost().c_str(), ControllerSettings.Port); - } else { - MQTTclient_037->setServer(ControllerSettings.getIP(), ControllerSettings.Port); + { + MakeControllerSettings(ControllerSettings); + if (!AllocatedControllerSettings()) { + addLog(LOG_LEVEL_ERROR, F("IMPT : Cannot load controller settings, out of RAM")); + return false; + } + + LoadControllerSettings(enabledMqttController, ControllerSettings); + if (ControllerSettings.UseDNS) { + MQTTclient_037->setServer(ControllerSettings.getHost().c_str(), ControllerSettings.Port); + } else { + MQTTclient_037->setServer(ControllerSettings.getIP(), ControllerSettings.Port); + } + MQTTclient_037->setCallback(mqttcallback_037); + if (hasControllerCredentialsSet(enabledMqttController, ControllerSettings)) { + hasCredentials = true; + user = getControllerUser(enabledMqttController, ControllerSettings); + pass = getControllerPass(enabledMqttController, ControllerSettings); + } } - MQTTclient_037->setCallback(mqttcallback_037); // Try three times for a connection - for (byte x = 1; x < 4; x++) { String log = ""; - if (hasControllerCredentialsSet(enabledMqttController, ControllerSettings)) - result = MQTTclient_037->connect(clientid.c_str(), - getControllerUser(enabledMqttController, ControllerSettings).c_str(), - getControllerPass(enabledMqttController, ControllerSettings).c_str()); - else + if (hasCredentials) { + result = MQTTclient_037->connect(clientid.c_str(), user.c_str(), pass.c_str()); + } else { result = MQTTclient_037->connect(clientid.c_str()); - + } if (result) { diff --git a/src/define_plugin_sets.h b/src/define_plugin_sets.h index 8b61564eb2..32705c16fd 100644 --- a/src/define_plugin_sets.h +++ b/src/define_plugin_sets.h @@ -1107,6 +1107,15 @@ To create/register a plugin, you have to : #ifdef FEATURE_I2CMULTIPLEXER #undef FEATURE_I2CMULTIPLEXER #endif + #ifdef USE_SERVO + #undef USE_SERVO + #endif + #ifdef USES_BLYNK + #undef USES_BLYNK + #endif + #ifdef USES_C017 // Zabbix + #undef USES_C017 + #endif #endif // Timing stats page needs timing stats @@ -1130,5 +1139,15 @@ To create/register a plugin, you have to : #endif +// It may have gotten undefined to fit a build. Make sure the Blynk controllers are not defined +#ifndef USES_BLYNK + #ifdef USES_C012 + #undef USES_C012 + #endif + #ifdef USES_C015 + #undef USES_C015 + #endif +#endif + #endif // DEFINE_PLUGIN_SETS_H diff --git a/src/src/Commands/Blynk.cpp b/src/src/Commands/Blynk.cpp index 4dc5ebfb9a..5832b5f359 100644 --- a/src/src/Commands/Blynk.cpp +++ b/src/src/Commands/Blynk.cpp @@ -66,41 +66,50 @@ String Command_Blynk_Get(struct EventStruct *event, const char *Line) bool Blynk_get(const String& command, controllerIndex_t controllerIndex, float *data) { - MakeControllerSettings(ControllerSettings); - if (!AllocatedControllerSettings()) { - addLog(LOG_LEVEL_ERROR, F("Blynk : Cannot run GET, out of RAM")); - return false; - } + bool MustCheckReply = false; + String hostname, pass; + unsigned int ClientTimeout = 0; + WiFiClient client; - LoadControllerSettings(controllerIndex, ControllerSettings); + { + // Place ControllerSettings in its own scope, as it is quite big. + MakeControllerSettings(ControllerSettings); + if (!AllocatedControllerSettings()) { + addLog(LOG_LEVEL_ERROR, F("Blynk : Cannot run GET, out of RAM")); + return false; + } - if ((getControllerPass(controllerIndex, ControllerSettings).length() == 0)) { - addLog(LOG_LEVEL_ERROR, F("Blynk : No password set")); - return false; - } + LoadControllerSettings(controllerIndex, ControllerSettings); + MustCheckReply = ControllerSettings.MustCheckReply; + hostname = ControllerSettings.getHost(); + pass = getControllerPass(controllerIndex, ControllerSettings); + ClientTimeout = ControllerSettings.ClientTimeout; - WiFiClient client; + if (pass.length() == 0) { + addLog(LOG_LEVEL_ERROR, F("Blynk : No password set")); + return false; + } - if (!try_connect_host(/* CPLUGIN_ID_012 */ 12, client, ControllerSettings)) { - return false; + if (!try_connect_host(/* CPLUGIN_ID_012 */ 12, client, ControllerSettings)) { + return false; + } } - // We now create a URI for the request { // Place this stack allocated array in its own scope, as it is quite big. char request[300] = { 0 }; sprintf_P(request, PSTR("GET /%s/%s HTTP/1.1\r\n Host: %s \r\n Connection: close\r\n\r\n"), - getControllerPass(controllerIndex, ControllerSettings).c_str(), + pass.c_str(), command.c_str(), - ControllerSettings.getHost().c_str()); + hostname.c_str()); addLog(LOG_LEVEL_DEBUG, request); client.print(request); } - bool success = !ControllerSettings.MustCheckReply; + bool success = !MustCheckReply; - if (ControllerSettings.MustCheckReply || data) { + if (MustCheckReply || data) { unsigned long timer = millis() + 200; while (!client_available(client) && !timeOutReached(timer)) { @@ -155,7 +164,7 @@ bool Blynk_get(const String& command, controllerIndex_t controllerIndex, float * client.stop(); // important - backgroundtasks - free mem - unsigned long timer = millis() + ControllerSettings.ClientTimeout; + unsigned long timer = millis() + ClientTimeout; while (!timeOutReached(timer)) { backgroundtasks(); diff --git a/src/src/ControllerQueue/C011_queue_element.cpp b/src/src/ControllerQueue/C011_queue_element.cpp new file mode 100644 index 0000000000..dac10333ec --- /dev/null +++ b/src/src/ControllerQueue/C011_queue_element.cpp @@ -0,0 +1,20 @@ +#include "../ControllerQueue/C011_queue_element.h" + +#include "../DataStructs/ESPEasy_EventStruct.h" + +C011_queue_element::C011_queue_element() {} + +C011_queue_element::C011_queue_element(const struct EventStruct *event) : + idx(event->idx), + TaskIndex(event->TaskIndex), + controller_idx(event->ControllerIndex), + sensorType(event->sensorType) {} + +size_t C011_queue_element::getSize() const { + size_t total = sizeof(*this); + total += uri.length(); + total += HttpMethod.length(); + total += header.length(); + total += postStr.length(); + return total; +} diff --git a/src/src/ControllerQueue/C011_queue_element.h b/src/src/ControllerQueue/C011_queue_element.h new file mode 100644 index 0000000000..115dcb4966 --- /dev/null +++ b/src/src/ControllerQueue/C011_queue_element.h @@ -0,0 +1,39 @@ +#ifndef CONTROLLERQUEUE_C011_QUEUE_ELEMENT_H +#define CONTROLLERQUEUE_C011_QUEUE_ELEMENT_H + +#include "../../ESPEasy_common.h" +#include "../DataStructs/ESPEasyLimits.h" +#include "../Globals/CPlugins.h" +#include "../Globals/Plugins.h" + +struct EventStruct; + + +// #ifdef USES_C011 + +/*********************************************************************************************\ +* C011_queue_element for queueing requests for C011: Generic HTTP Advanced. +\*********************************************************************************************/ +class C011_queue_element { +public: + + C011_queue_element(); + + C011_queue_element(const struct EventStruct *event); + + size_t getSize() const; + + String uri; + String HttpMethod; + String header; + String postStr; + int idx = 0; + taskIndex_t TaskIndex = INVALID_TASK_INDEX; + controllerIndex_t controller_idx = INVALID_CONTROLLER_INDEX; + byte sensorType = 0; +}; + +// #endif //USES_C011 + + +#endif // CONTROLLERQUEUE_C011_QUEUE_ELEMENT_H diff --git a/src/src/ControllerQueue/DelayQueueElements.cpp b/src/src/ControllerQueue/DelayQueueElements.cpp index 87f6f83d28..04e82d729b 100644 --- a/src/src/ControllerQueue/DelayQueueElements.cpp +++ b/src/src/ControllerQueue/DelayQueueElements.cpp @@ -87,7 +87,6 @@ DEFINE_Cxxx_DELAY_QUEUE_MACRO_CPP( 0, 10) * C011_queue_element for queueing requests for 011: Generic HTTP Advanced \*********************************************************************************************/ #ifdef USES_C011 -# define C011_queue_element simple_queue_element_string_only DEFINE_Cxxx_DELAY_QUEUE_MACRO_CPP( 0, 11) #endif // ifdef USES_C011 diff --git a/src/src/ControllerQueue/DelayQueueElements.h b/src/src/ControllerQueue/DelayQueueElements.h index 8bc0935b7d..2282c8fc90 100644 --- a/src/src/ControllerQueue/DelayQueueElements.h +++ b/src/src/ControllerQueue/DelayQueueElements.h @@ -93,7 +93,7 @@ DEFINE_Cxxx_DELAY_QUEUE_MACRO( 0, 10) * C011_queue_element for queueing requests for 011: Generic HTTP Advanced \*********************************************************************************************/ #ifdef USES_C011 -# define C011_queue_element simple_queue_element_string_only +# include "../ControllerQueue/C011_queue_element.h" DEFINE_Cxxx_DELAY_QUEUE_MACRO( 0, 11) #endif // ifdef USES_C011 diff --git a/src/src/DataStructs/ControllerSettingsStruct.cpp b/src/src/DataStructs/ControllerSettingsStruct.cpp index cc6ccbed2f..3f1a7e3caa 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.cpp +++ b/src/src/DataStructs/ControllerSettingsStruct.cpp @@ -25,7 +25,7 @@ void ControllerSettingsStruct::reset() { ClientTimeout = CONTROLLER_CLIENTTIMEOUT_DFLT; MustCheckReply = false; SampleSetInitiator = INVALID_TASK_INDEX; - MQTT_flags = 0; + VariousFlags = 0; for (byte i = 0; i < 4; ++i) { IP[i] = 0; @@ -184,60 +184,70 @@ bool ControllerSettingsStruct::updateIPcache() { bool ControllerSettingsStruct::mqtt_cleanSession() const { - return bitRead(MQTT_flags, 1); + return bitRead(VariousFlags, 1); } void ControllerSettingsStruct::mqtt_cleanSession(bool value) { - bitWrite(MQTT_flags, 1, value); + bitWrite(VariousFlags, 1, value); } bool ControllerSettingsStruct::mqtt_sendLWT() const { - return !bitRead(MQTT_flags, 2); + return !bitRead(VariousFlags, 2); } void ControllerSettingsStruct::mqtt_sendLWT(bool value) { - bitWrite(MQTT_flags, 2, !value); + bitWrite(VariousFlags, 2, !value); } bool ControllerSettingsStruct::mqtt_willRetain() const { - return !bitRead(MQTT_flags, 3); + return !bitRead(VariousFlags, 3); } void ControllerSettingsStruct::mqtt_willRetain(bool value) { - bitWrite(MQTT_flags, 3, !value); + bitWrite(VariousFlags, 3, !value); } bool ControllerSettingsStruct::mqtt_uniqueMQTTclientIdReconnect() const { - return bitRead(MQTT_flags, 4); + return bitRead(VariousFlags, 4); } void ControllerSettingsStruct::mqtt_uniqueMQTTclientIdReconnect(bool value) { - bitWrite(MQTT_flags, 4, value); + bitWrite(VariousFlags, 4, value); } bool ControllerSettingsStruct::mqtt_retainFlag() const { - return bitRead(MQTT_flags, 5); + return bitRead(VariousFlags, 5); } void ControllerSettingsStruct::mqtt_retainFlag(bool value) { - bitWrite(MQTT_flags, 5, value); + bitWrite(VariousFlags, 5, value); } bool ControllerSettingsStruct::useExtendedCredentials() const { - return bitRead(MQTT_flags, 6); + return bitRead(VariousFlags, 6); } void ControllerSettingsStruct::useExtendedCredentials(bool value) { - bitWrite(MQTT_flags, 6, value); + bitWrite(VariousFlags, 6, value); +} + +bool ControllerSettingsStruct::sendBinary() const +{ + return bitRead(VariousFlags, 7); +} + +void ControllerSettingsStruct::sendBinary(bool value) +{ + bitWrite(VariousFlags, 7, value); } diff --git a/src/src/DataStructs/ControllerSettingsStruct.h b/src/src/DataStructs/ControllerSettingsStruct.h index 9d66a628be..ff2a8f5624 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.h +++ b/src/src/DataStructs/ControllerSettingsStruct.h @@ -41,7 +41,7 @@ class WiFiUDP; // Timeout of the client in msec. #ifndef CONTROLLER_CLIENTTIMEOUT_MAX -# define CONTROLLER_CLIENTTIMEOUT_MAX 1000 +# define CONTROLLER_CLIENTTIMEOUT_MAX 4000 // Not sure if this may trigger SW watchdog. #endif // ifndef CONTROLLER_CLIENTTIMEOUT_MAX #ifndef CONTROLLER_CLIENTTIMEOUT_DFLT # define CONTROLLER_CLIENTTIMEOUT_DFLT 100 @@ -82,6 +82,7 @@ struct ControllerSettingsStruct CONTROLLER_CLEAN_SESSION, CONTROLLER_TIMEOUT, CONTROLLER_SAMPLE_SET_INITIATOR, + CONTROLLER_SEND_BINARY, // Keep this as last, is used to loop over all parameters CONTROLLER_ENABLED @@ -110,7 +111,7 @@ struct ControllerSettingsStruct String getHostPortString() const; - // MQTT_flags defaults to 0, keep in mind when adding bit lookups. + // VariousFlags defaults to 0, keep in mind when adding bit lookups. bool mqtt_cleanSession() const; void mqtt_cleanSession(bool value); @@ -129,6 +130,9 @@ struct ControllerSettingsStruct bool useExtendedCredentials() const; void useExtendedCredentials(bool value); + bool sendBinary() const; + void sendBinary(bool value); + boolean UseDNS; byte IP[4]; unsigned int Port; @@ -145,7 +149,7 @@ struct ControllerSettingsStruct unsigned int ClientTimeout; bool MustCheckReply; // When set to false, a sent message is considered always successful. taskIndex_t SampleSetInitiator; // The first task to start a sample set. - uint32_t MQTT_flags; // Various flags for MQTT controllers + uint32_t VariousFlags; // Various flags char ClientID[65]; // Used to define the Client ID used by the controller private: