diff --git a/.gitignore b/.gitignore index 718428ff73..e10f59014d 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ ## Project ##################### lib/readme.txt src/Custom.h +test/output_export.cpp diff --git a/dist/Release_notes.txt b/dist/Release_notes.txt index f5e12982bf..758c1e6e48 100644 --- a/dist/Release_notes.txt +++ b/dist/Release_notes.txt @@ -1,3 +1,100 @@ +------------------------------------------------- +Changes in release mega-20180501 (since mega-20180430) +------------------------------------------------- + +Release date: Tue May 1 04:00:17 CEST 2018 + +mvdbro (1): + Fix broken Mini Dashboard feature + + +------------------------------------------------- +Changes in release mega-20180430 (since mega-20180429) +------------------------------------------------- + +Release date: Mon Apr 30 04:00:08 CEST 2018 + +TD-er (4): + [wifi] Do not rely on WiFi.status() and better log of status + [wifi] Use internal ESPeasy state to check connected status + [rules] Add log indicating POST request for update rules + [MQTT] Set default timeout to 10 sec, equal to default Mosquito timeout + +mvdbro (1): + Changed plugin call debug timers + + +------------------------------------------------- +Changes in release mega-20180429 (since mega-20180428) +------------------------------------------------- + +Release date: Sun Apr 29 04:00:07 CEST 2018 + +TD-er (7): + [wifi] Setup static IP config after connecting to wifi. + [wifi] Set static IP config before and after connect + [wifi] Show number of reconnects in the sysinfo + [wifi] Add wifi status log (Debug More) and DHCP/Static config to info page + [wifi] ESP32 does not know wifi_station_get_connect_status() + [wifi] Added connectionCheckHandler() to force reconnect when needed + [wifi] Force wifi reconnect at lots of MQTT failed connects. + + +------------------------------------------------- +Changes in release mega-20180428 (since mega-20180426) +------------------------------------------------- + +Release date: Sat Apr 28 04:00:15 CEST 2018 + +TD-er (4): + [wifi] Attempt to make event based wifi simpler + [PlatformIO] Updated core to 2.4.1 + [wifi] Just disconnect when DNS lookup is not possible + [Wifi] Avoid doing network communications when not connected. + + +------------------------------------------------- +Changes in release mega-20180426 (since mega-20180425) +------------------------------------------------- + +Release date: Thu Apr 26 04:00:14 CEST 2018 + +Gijs Noorlander (1): + Change use of core 2_4_0 to 2_3_0 + + +------------------------------------------------- +Changes in release mega-20180425 (since mega-20180424) +------------------------------------------------- + +Release date: Wed Apr 25 04:00:08 CEST 2018 + +Grovkillen (4): + Added a void addCopyButton + Made copy to clipboard JavaScript based instead + Added copy to clipboard on log page + Added new lines if BR + delimiter if wanted + +Plebs (3): + Fix for #1300 + Adding 3 new http commands: taskrun, taskvalueset and rules + Fix another bug in LCD plugin + +TD-er (2): + [Modbus] PR #1128 made by @s0170071 + [wifi] More active reconnect to wifi when disconnected + + +------------------------------------------------- +Changes in release mega-20180424 (since mega-20180423) +------------------------------------------------- + +Release date: Tue Apr 24 04:00:13 CEST 2018 + +Grovkillen (1): + Added note to self compilers + + ------------------------------------------------- Changes in release mega-20180423 (since mega-20180422) ------------------------------------------------- diff --git a/lib/MechInputs/QEIx4.cpp b/lib/MechInputs/QEIx4.cpp index a7de128c0d..066ea096ee 100644 --- a/lib/MechInputs/QEIx4.cpp +++ b/lib/MechInputs/QEIx4.cpp @@ -151,7 +151,7 @@ void QEIx4::begin(int16_t pinA, int16_t pinB, int16_t pinI, uint8_t mode) _pinI = pinI; _counter = 0; - _bHasChanged = true; + _bHasChanged = false; if (mode == 1) _eventMask = QEIx4_1x_MASK; diff --git a/lib/pubsubclient/src/PubSubClient.h b/lib/pubsubclient/src/PubSubClient.h index c6f8e67575..ed92af82ce 100644 --- a/lib/pubsubclient/src/PubSubClient.h +++ b/lib/pubsubclient/src/PubSubClient.h @@ -27,8 +27,9 @@ #endif // MQTT_KEEPALIVE : keepAlive interval in Seconds +// Keepalive timeout for default MQTT Broker is 10s #ifndef MQTT_KEEPALIVE -#define MQTT_KEEPALIVE 15 +#define MQTT_KEEPALIVE 10 #endif // MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds diff --git a/platformio.ini b/platformio.ini index b22feddb1a..b73ca35bd9 100644 --- a/platformio.ini +++ b/platformio.ini @@ -39,12 +39,15 @@ platform = espressif8266@1.5.0 [core_2_4_0] platform = espressif8266@1.6.0 +[core_2_4_1] +platform = espressif8266@1.7.0 + [core_staged] platform = https://github.com/platformio/platform-espressif8266.git#feature/stage [core_esp32] -#platform = espressif32@0.12.0 -platform = https://github.com/platformio/platform-espressif32.git#feature/stage +platform = espressif32@0.12.0 +#platform = https://github.com/platformio/platform-espressif32.git#feature/stage build_flags = -D BUILD_GIT='"${env.TRAVIS_TAG}"' lib_ignore = ESPeasySoftwareSerial, EspESPeasySoftwareSerial, AS_BH1750, ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266HTTPUpdateServer, ESP8266mDNS, IRremoteESP8266, ESPEasy_ESP8266Ping, SerialSensors lib_deps = ESP32WebServer @@ -52,22 +55,23 @@ lib_deps = ESP32WebServer [common] build_flags = -D BUILD_GIT='"${env.TRAVIS_TAG}"' ; ${compiler_warnings.build_flags} + -D PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH lib_deps = "" lib_ignore = ESP32_ping, ESP32WebServer lib_ldf_mode = chain upload_speed = 460800 framework = arduino board = esp12e -platform = ${core_2_4_0.platform} +platform = ${core_2_4_1.platform} [normal] platform = ${common.platform} [testing] -platform = ${core_2_4_0.platform} +platform = ${core_2_4_1.platform} [dev] -platform = ${core_2_4_0.platform} +platform = ${core_2_4_1.platform} [esp8266_1M] @@ -83,13 +87,13 @@ build_flags = ${esp8266_1M.build_flags} -DESP8285 board = esp01_1m board_flash_mode = ${esp8266_1M.board_flash_mode} build_flags = ${common.build_flags} -Wl,-Tesp8266.flash.1m128.ld -platform = ${core_2_4_0.platform} +platform = ${core_2_4_1.platform} [Sonoff_8285] board_flash_mode = ${esp8285_1M.board_flash_mode} board = ${esp8285_1M.board} build_flags = ${esp8285_1M.build_flags} -platform = ${core_2_4_0.platform} +platform = ${core_2_4_1.platform} [espWroom2M] board_flash_mode = dout diff --git a/src/Command.ino b/src/Command.ino index d177cbbf75..27a2866dcf 100644 --- a/src/Command.ino +++ b/src/Command.ino @@ -487,7 +487,7 @@ void ExecuteCommand(byte source, const char *Line) } break; } - + case cmd_TimerPause: { if (Par1>=1 && Par1<=RULES_TIMER_MAX) @@ -500,7 +500,7 @@ void ExecuteCommand(byte source, const char *Line) { String event = F("Rules#TimerPause="); event += Par1; - rulesProcessing(event); + rulesProcessing(event); RulesTimer[Par1 - 1].paused = true; RulesTimer[Par1 - 1].interval = -delta; // set remaind time } @@ -514,9 +514,9 @@ void ExecuteCommand(byte source, const char *Line) { addLog(LOG_LEVEL_ERROR, F("TIMER: invalid timer number")); } - break; + break; } - + case cmd_TimerResume: { if (Par1>=1 && Par1<=RULES_TIMER_MAX) @@ -528,9 +528,9 @@ void ExecuteCommand(byte source, const char *Line) { String event = F("Rules#TimerResume="); event += Par1; - rulesProcessing(event); + rulesProcessing(event); RulesTimer[Par1 - 1].timestamp = millis() + (RulesTimer[Par1 - 1].interval); - RulesTimer[Par1 - 1].paused = false; + RulesTimer[Par1 - 1].paused = false; } } else @@ -542,9 +542,9 @@ void ExecuteCommand(byte source, const char *Line) { addLog(LOG_LEVEL_ERROR, F("TIMER: invalid timer number")); } - break; + break; } - + case cmd_Delay: { success = true; @@ -748,7 +748,7 @@ void ExecuteCommand(byte source, const char *Line) case cmd_WifiConnect: { success = true; - setWifiState(WifiTryConnect); + WiFiConnectRelaxed(); break; } @@ -761,7 +761,7 @@ void ExecuteCommand(byte source, const char *Line) case cmd_WifiAPMode: { - setWifiState(WifiEnableAP); + setAP(true); success = true; break; } diff --git a/src/Controller.ino b/src/Controller.ino index 31ce5282c0..f93dc2c02d 100644 --- a/src/Controller.ino +++ b/src/Controller.ino @@ -89,6 +89,7 @@ void callback(char* c_topic, byte* b_payload, unsigned int length) { strncpy(c_payload,(char*)b_payload,length); c_payload[length] = 0; +/* String log; log=F("MQTT : Topic: "); log+=c_topic; @@ -97,6 +98,7 @@ void callback(char* c_topic, byte* b_payload, unsigned int length) { log=F("MQTT : Payload: "); log+=c_payload; addLog(LOG_LEVEL_DEBUG_MORE, log); + */ // sprintf_P(log, PSTR("%s%s"), "MQTT : Topic: ", c_topic); // addLog(LOG_LEVEL_DEBUG, log); @@ -116,6 +118,7 @@ void callback(char* c_topic, byte* b_payload, unsigned int length) { \*********************************************************************************************/ bool MQTTConnect(int controller_idx) { + ++mqtt_reconnect_count; ControllerSettingsStruct ControllerSettings; LoadControllerSettings(controller_idx, (byte*)&ControllerSettings, sizeof(ControllerSettings)); if (!ControllerSettings.checkHostReachable(true)) @@ -170,6 +173,7 @@ bool MQTTConnect(int controller_idx) if (MQTTclient.publish(LWTTopic.c_str(), "Connected", 1)) { updateMQTTclient_connected(); statusLED(true); + mqtt_reconnect_count = 0; return true; // end loop if succesfull } return false; diff --git a/src/ESPEasy-Globals.h b/src/ESPEasy-Globals.h index 056ddb47da..bede3ab08e 100644 --- a/src/ESPEasy-Globals.h +++ b/src/ESPEasy-Globals.h @@ -1,3 +1,5 @@ +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) #ifndef ESPEASY_GLOBALS_H_ #define ESPEASY_GLOBALS_H_ @@ -477,6 +479,7 @@ WiFiClient mqtt; PubSubClient MQTTclient(mqtt); bool MQTTclient_should_reconnect = true; bool MQTTclient_connected = false; +int mqtt_reconnect_count = 0; // udp protocol stuff (syslog, global sync, node info list, ntp time) WiFiUDP portUDP; @@ -556,6 +559,7 @@ enum Command { // Forward declarations. Command commandStringToEnum(const char * cmd); bool WiFiConnected(uint32_t timeout_ms); +bool WiFiConnected(); bool hostReachable(const IPAddress& ip); bool hostReachable(const String& hostname); void formatMAC(const uint8_t* mac, char (&strMAC)[20]); @@ -824,6 +828,7 @@ struct ControllerSettingsStruct if (!UseDNS) { return true; } + if (!WiFiConnected()) return false; IPAddress tmpIP; if (WiFi.hostByName(HostName, tmpIP)) { for (byte x = 0; x < 4; x++) { @@ -1072,6 +1077,8 @@ struct systemTimerStruct byte Par3; } systemTimers[SYSTEM_TIMER_MAX]; +#define NOTAVAILABLE_SYSTEM_TIMER_ERROR "There are no system timer available, max parallel timers are " STR(SYSTEM_TIMER_MAX) + struct systemCMDTimerStruct { systemCMDTimerStruct() : timer(0) {} @@ -1193,23 +1200,25 @@ enum WiFiDisconnectReason }; #endif -enum WifiState { - WifiOff, - WifiStart, - WifiTryConnect, - WifiConnectionFailed, - WifiClientConnectAP, - WifiClientDisconnectAP, - WifiCredentialsChanged, - WifiConnectSuccess, - WifiDisableAP, - WifiEnableAP, - WifiStartScan, -}; -WifiState currentWifiState = WifiStart; +#ifndef ESP32 +// To do some reconnection check. +#include +Ticker connectionCheck; +#endif + +bool reconnectChecker = false; +void connectionCheckHandler() +{ + if (reconnectChecker == false && !WiFiConnected()){ + reconnectChecker = true; + WiFi.reconnect(); + } + else if (WiFiConnected() && reconnectChecker == true){ + reconnectChecker = false; + } +} -void setWifiState(WifiState state); bool useStaticIP(); // WiFi related data @@ -1219,6 +1228,7 @@ uint8_t lastBSSID[6] = {0}; uint8_t wifiStatus = ESPEASY_WIFI_DISCONNECTED; unsigned long last_wifi_connect_attempt_moment = 0; unsigned int wifi_connect_attempt = 0; +int wifi_reconnects = -1; // First connection attempt is not a reconnect. uint8_t lastWiFiSettings = 0; String last_ssid; bool bssid_changed = false; @@ -1244,13 +1254,13 @@ bool processedGetIP = true; bool processedConnectAPmode = true; bool processedDisconnectAPmode = true; bool processedScanDone = true; -bool processedTryConnect = true; bool webserver_state = false; bool webserver_init = false; -unsigned long start = 0; -unsigned long elapsed = 0; +unsigned long elapsed10ps = 0; +unsigned long elapsed10psU = 0; +unsigned long elapsed50ps = 0; unsigned long loopCounter = 0; unsigned long loopCounterLast = 0; unsigned long loopCounterMax = 1; @@ -1269,6 +1279,8 @@ boolean activeRuleSets[RULESETS_MAX]; boolean UseRTOSMultitasking; +void (*MainLoopCall_ptr)(void); + // These wifi event functions must be in a .h-file because otherwise the preprocessor // may not filter the ifdef checks properly. // Also the functions use a lot of global defined variables, so include at the end of this file. diff --git a/src/ESPEasy.ino b/src/ESPEasy.ino index b1e0259517..391f35c464 100644 --- a/src/ESPEasy.ino +++ b/src/ESPEasy.ino @@ -134,13 +134,8 @@ void setup() String log = F("\n\n\rINIT : Booting version: "); log += BUILD_GIT; -#if defined(ESP32) - log += F(" (ESP32 SDK "); - log += ESP.getSdkVersion(); -#else - log += F(" (ESP82xx Core "); - log += ESP.getCoreVersion(); -#endif + log += F(" ("); + log += getSystemLibraryString(); log += F(")"); addLog(LOG_LEVEL_INFO, log); @@ -181,6 +176,7 @@ void setup() fileSystemCheck(); progMemMD5check(); LoadSettings(); + // setWifiMode(WIFI_STA); checkRuleSets(); @@ -241,22 +237,9 @@ void setup() NPluginInit(); log = F("INFO : Plugins: "); log += deviceCount + 1; - #ifdef PLUGIN_BUILD_NORMAL - log += F(" [Normal]"); - #endif - #ifdef PLUGIN_BUILD_TESTING - log += F(" [Testing]"); - #endif - #ifdef PLUGIN_BUILD_DEV - log += F(" [Development]"); - #endif - #if defined(ESP32) - log += F(" (ESP32 SDK "); - log += ESP.getSdkVersion(); - #else - log += F(" (ESP82xx Core "); - log += ESP.getCoreVersion(); - #endif + log += getPluginDescriptionString(); + log += F(" ("); + log += getSystemLibraryString(); log += F(")"); addLog(LOG_LEVEL_INFO, log); @@ -281,7 +264,7 @@ void setup() WifiScanAsync(); } */ - setWifiState(WifiTryConnect); + WiFiConnectRelaxed(); #ifdef FEATURE_REPORTING ReportStatus(); @@ -323,6 +306,9 @@ void setup() } #endif + #ifndef ESP32 + connectionCheck.attach(30, connectionCheckHandler); + #endif } #ifdef USE_RTOS_MULTITASKING @@ -376,20 +362,22 @@ bool getControllerProtocolDisplayName(byte ProtocolIndex, byte parameterIdx, Str \*********************************************************************************************/ void loop() { - //checkRAM(F("loop")); + if(MainLoopCall_ptr) + MainLoopCall_ptr(); + loopCounter++; if (wifiSetupConnect) { // try to connect for setup wizard - setWifiState(WifiCredentialsChanged); + WiFiConnectRelaxed(); wifiSetupConnect = false; } if (wifiStatus != ESPEASY_WIFI_SERVICES_INITIALIZED) { if (wifiStatus >= ESPEASY_WIFI_CONNECTED) processConnect(); if (wifiStatus >= ESPEASY_WIFI_GOT_IP) processGotIP(); if (wifiStatus == ESPEASY_WIFI_DISCONNECTED) processDisconnect(); - } else if (WiFi.status() != WL_CONNECTED) { + } else if (!WiFiConnected()) { // Somehow the WiFi has entered a limbo state. // FIXME TD-er: This may happen on WiFi config with AP_STA mode active. // addLog(LOG_LEVEL_ERROR, F("Wifi status out sync")); @@ -504,9 +492,9 @@ void updateMQTTclient_connected() { void run50TimesPerSecond() { timer20ms = millis() + 20; + unsigned long start = micros(); PluginCall(PLUGIN_FIFTY_PER_SECOND, 0, dummyString); - - // statusLED(false); + elapsed50ps += micros() - start; } /*********************************************************************************************\ @@ -514,16 +502,18 @@ void run50TimesPerSecond() \*********************************************************************************************/ void run10TimesPerSecond() { - start = micros(); timer100ms = millis() + 100; + unsigned long start = micros(); PluginCall(PLUGIN_TEN_PER_SECOND, 0, dummyString); + elapsed10ps += micros() - start; + start = micros(); PluginCall(PLUGIN_UNCONDITIONAL_POLL, 0, dummyString); + elapsed10psU += micros() - start; if (Settings.UseRules && eventBuffer.length() > 0) { rulesProcessing(eventBuffer); eventBuffer = ""; } - elapsed = micros() - start; #ifndef USE_RTOS_MULTITASKING WebServer.handleClient(); #endif @@ -581,15 +571,15 @@ void runOncePerSecond() if (Settings.UseNTP) checkTime(); - unsigned long timer = micros(); + unsigned long start = micros(); PluginCall(PLUGIN_ONCE_A_SECOND, 0, dummyString); + unsigned long elapsed = micros() - start; checkSystemTimers(); if (Settings.UseRules) rulesTimers(); - timer = micros() - timer; if (SecuritySettings.Password[0] != 0) { @@ -607,12 +597,20 @@ void runOncePerSecond() Wire.endTransmission(); } - if (Settings.SerialLogLevel == 5) + if (Settings.SerialLogLevel == LOG_LEVEL_DEBUG_DEV) { - Serial.print(F("10 ps:")); + Serial.print(F("Plugin calls: 50 ps:")); + Serial.print(elapsed50ps); + Serial.print(F(" uS, 10 ps:")); + Serial.print(elapsed10ps); + Serial.print(F(" uS, 10 psU:")); + Serial.print(elapsed10psU); + Serial.print(F(" uS, 1 ps:")); Serial.print(elapsed); - Serial.print(F(" uS 1 ps:")); - Serial.println(timer); + Serial.println(F(" uS")); + elapsed50ps=0; + elapsed10ps=0; + elapsed10psU=0; } checkResetFactoryPin(); } @@ -760,7 +758,9 @@ void setSystemTimer(unsigned long timer, byte plugin, byte Par1, byte Par2, byte // plugin number and par1 form a unique key that can be used to restart a timer // first check if a timer is not already running for this request boolean reUse = false; + byte firstAvailable = SYSTEM_TIMER_MAX; for (byte x = 0; x < SYSTEM_TIMER_MAX; x++) + { if (systemTimers[x].timer != 0) { if ((systemTimers[x].plugin == plugin) && (systemTimers[x].Par1 == Par1)) @@ -770,20 +770,25 @@ void setSystemTimer(unsigned long timer, byte plugin, byte Par1, byte Par2, byte break; } } - + else if(firstAvailable == SYSTEM_TIMER_MAX) + { + firstAvailable = x; + } + } if (!reUse) { - // find a new free timer slot... - for (byte x = 0; x < SYSTEM_TIMER_MAX; x++) - if (systemTimers[x].timer == 0) - { - systemTimers[x].timer = millis() + timer; - systemTimers[x].plugin = plugin; - systemTimers[x].Par1 = Par1; - systemTimers[x].Par2 = Par2; - systemTimers[x].Par3 = Par3; - break; - } + if (firstAvailable == SYSTEM_TIMER_MAX ) + { + addLog(LOG_LEVEL_ERROR, F(NOTAVAILABLE_SYSTEM_TIMER_ERROR)); + } + else + { + systemTimers[firstAvailable].timer = millis() + timer; + systemTimers[firstAvailable].plugin = plugin; + systemTimers[firstAvailable].Par1 = Par1; + systemTimers[firstAvailable].Par2 = Par2; + systemTimers[firstAvailable].Par3 = Par3; + } } } diff --git a/src/ESPEasyWifi.ino b/src/ESPEasyWifi.ino index fe73e2f3d4..38536e0c76 100644 --- a/src/ESPEasyWifi.ino +++ b/src/ESPEasyWifi.ino @@ -8,6 +8,7 @@ void processConnect() { if (processedConnect) return; processedConnect = true; + ++wifi_reconnects; if (wifiStatus < ESPEASY_WIFI_CONNECTED) return; const long connect_duration = timeDiff(last_wifi_connect_attempt_moment, lastConnectMoment); String log = F("WIFI : Connected! AP: "); @@ -27,7 +28,11 @@ void processConnect() { String event = F("WiFi#ChangedAccesspoint"); rulesProcessing(event); } - setWifiState(WifiConnectSuccess); + if (useStaticIP()) { + setupStaticIPconfig(); + markGotIP(); + } + logConnectionStatus(); } void processDisconnect() { @@ -45,50 +50,18 @@ void processDisconnect() { log += format_msec_duration(lastConnectedDuration); } addLog(LOG_LEVEL_INFO, log); - if (wifi_connect_attempt > 5) { - setWifiState(WifiConnectionFailed); - } else { - switch (lastDisconnectReason) { - case WIFI_DISCONNECT_REASON_NO_AP_FOUND: - case WIFI_DISCONNECT_REASON_AUTH_FAIL: - case WIFI_DISCONNECT_REASON_ASSOC_FAIL: - case WIFI_DISCONNECT_REASON_HANDSHAKE_TIMEOUT: - // Disconnect without chance for success on next attempt - if (selectNextWiFiSettings()) { - if (!intent_to_reboot) - setWifiState(WifiTryConnect); - return; - } else { - if (Settings.deepSleep && Settings.deepSleepOnFail) { - //only one attempt in deepsleep, to conserve battery - addLog(LOG_LEVEL_ERROR, F("SLEEP: Connection failed, going back to sleep.")); - deepSleep(Settings.Delay); - } - setWifiState(WifiConnectionFailed); - return; - } - break; - default: - setWifiState(WifiStart); - if (!intent_to_reboot) - setWifiState(WifiTryConnect); - return; - } - } + logConnectionStatus(); } void processGotIP() { if (processedGetIP) return; - processedGetIP = true; - // FIXME @TD-er: Disabled this check for now. - // It may be a possibility the events are processed out of order -// if (wifiStatus < ESPEASY_WIFI_GOT_IP) -// return; IPAddress ip = WiFi.localIP(); - if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) - return; + if (!useStaticIP()) + if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) + return; + processedGetIP = true; const IPAddress gw = WiFi.gatewayIP(); const IPAddress subnet = WiFi.subnetMask(); String log = F("WIFI : "); @@ -143,6 +116,7 @@ void processGotIP() { if (Settings.UseNTP) { initTime(); } + mqtt_reconnect_count = 0; timermqtt_interval = 100; timermqtt = millis() + timermqtt_interval; if (Settings.UseRules) @@ -154,11 +128,13 @@ void processGotIP() { // WiFi.scanDelete(); wifiStatus = ESPEASY_WIFI_SERVICES_INITIALIZED; setWebserverRunning(true); + wifi_connect_attempt = 0; if (wifiSetup) { // Wifi setup was active, Apparently these settings work. wifiSetup = false; SaveSettings(); } + logConnectionStatus(); } void processConnectAPmode() { @@ -169,7 +145,15 @@ void processConnectAPmode() { log += F(" Connected devices: "); log += WiFi.softAPgetStationNum(); addLog(LOG_LEVEL_INFO, log); - setWifiState(WifiClientConnectAP); + timerAPoff = 0; // Disable timer to switch AP off + setWebserverRunning(true); + // Start DNS, only used if the ESP has no valid WiFi config + // It will reply with it's own address on all DNS requests + // (captive portal concept) + if (!dnsServerActive) { + dnsServerActive = true; + dnsServer.start(DNS_PORT, "*", apIP); + } } void processDisconnectAPmode() { @@ -182,7 +166,17 @@ void processDisconnectAPmode() { log += nrStationsConnected; addLog(LOG_LEVEL_INFO, log); if (nrStationsConnected == 0) { - setWifiState(WifiClientDisconnectAP); + timerAPoff = millis() + WIFI_AP_OFF_TIMER_DURATION; + } +} + +void processDisableAPmode() { + if (timerAPoff == 0) return; + if (WifiIsAP(WiFi.getMode())) { + // disable AP after timeout. + if (timeOutReached(timerAPoff)) { + setAP(false); + } } } @@ -224,21 +218,19 @@ void processScanDone() { } } } - setWifiState(WifiTryConnect); } } void resetWiFi() { addLog(LOG_LEVEL_INFO, F("Reset WiFi.")); - setWifiState(WifiOff); - setWifiState(WifiStart); + setWifiMode(WIFI_OFF); + setWifiMode(WIFI_STA); lastDisconnectMoment = millis(); processedDisconnect = false; wifiStatus = ESPEASY_WIFI_DISCONNECTED; } void WifiScanAsync() { - setWifiState(WifiStartScan); addLog(LOG_LEVEL_INFO, F("WIFI : Start network scan")); #ifdef ESP32 bool async = true; @@ -284,90 +276,92 @@ bool WifiIsSTA(WiFiMode_t wifimode) #endif } -void setWifiState(WifiState state) { - currentWifiState = state; - // We are changing the state, so stop retry to connect, unless it is needed. - processedTryConnect = true; - switch (state) { - case WifiOff: - setWebserverRunning(false); - WifiDisconnect(); - changeWifiMode(WIFI_OFF); + +//******************************************************************************** +// Set Wifi Mode +//******************************************************************************** +void setSTA(bool enable) { + switch(WiFi.getMode()) { + case WIFI_OFF: + if (enable) setWifiMode(WIFI_STA); break; - case WifiStart: - changeWifiMode(WIFI_STA); + case WIFI_STA: + if (!enable) setWifiMode(WIFI_OFF); break; - case WifiConnectionFailed: - addLog(LOG_LEVEL_INFO, F("WIFI : Connection Failed")); - if (WIFI_AP != WiFi.getMode()) { - setWebserverRunning(false); - changeWifiMode(WIFI_AP); - wifiSetup = true; - timerAPoff = millis() + WIFI_AP_OFF_TIMER_DURATION; - } + case WIFI_AP: + if (enable) setWifiMode(WIFI_AP_STA); break; - case WifiClientConnectAP: - timerAPoff = 0; // Disable timer to switch AP off. - changeWifiMode(WIFI_AP); - setWebserverRunning(true); + case WIFI_AP_STA: + if (!enable) setWifiMode(WIFI_AP); break; - case WifiClientDisconnectAP: - if (WifiIsAP(WiFi.getMode())) { - timerAPoff = millis() + WIFI_AP_OFF_TIMER_DURATION; - } + default: break; - case WifiCredentialsChanged: - lastWiFiSettings = 0; // Force to load the first settings. - wifi_connect_attempt = 0; - // fall through - case WifiTryConnect: - if (WifiIsAP(WiFi.getMode())) { - timerAPoff = 0; // Disable timer to switch AP off. - changeWifiMode(WIFI_AP_STA); - } else { - changeWifiMode(WIFI_STA); - } - if (prepareWiFi()) { - processedTryConnect = false; - } else { - setWifiState(WifiConnectionFailed); - } + } +} + +void setAP(bool enable) { + switch(WiFi.getMode()) { + case WIFI_OFF: + if (enable) setWifiMode(WIFI_AP); break; - case WifiConnectSuccess: - if (WifiIsAP(WiFi.getMode())) { - timerAPoff = millis() + WIFI_AP_OFF_TIMER_DURATION; - } else { - timerAPoff = 0; // Disable timer to switch AP off. - changeWifiMode(WIFI_STA); - } - wifi_connect_attempt = 0; + case WIFI_STA: + if (enable) setWifiMode(WIFI_AP_STA); break; - case WifiDisableAP: - if (WifiIsAP(WiFi.getMode())) { - timerAPoff = 0; // Disable timer to switch AP off. - changeWifiMode(WIFI_STA); - } + case WIFI_AP: + if (!enable) setWifiMode(WIFI_OFF); break; - case WifiEnableAP: - if (wifiStatus == ESPEASY_WIFI_SERVICES_INITIALIZED) { - changeWifiMode(WIFI_AP_STA); - } else { - changeWifiMode(WIFI_AP); - } - timerAPoff = millis() + WIFI_AP_OFF_TIMER_DURATION; + case WIFI_AP_STA: + if (!enable) setWifiMode(WIFI_STA); break; - case WifiStartScan: - if (WifiIsAP(WiFi.getMode())) { - changeWifiMode(WIFI_AP_STA); - } else changeWifiMode(WIFI_STA); + default: break; } + if (enable) { + timerAPoff = millis() + WIFI_AP_OFF_TIMER_DURATION; + // create and store unique AP SSID/PW to prevent ESP from starting AP mode with default SSID and No password! + // setup ssid for AP Mode when needed + String softAPSSID=WifiGetAPssid(); + String pwd = SecuritySettings.WifiAPKey; + IPAddress subnet(DEFAULT_AP_SUBNET); + if (!WiFi.softAPConfig(apIP, apIP, subnet)) { + addLog(LOG_LEVEL_ERROR, "WIFI : [AP] softAPConfig failed!"); + } + if (WiFi.softAP(softAPSSID.c_str(),pwd.c_str())) { + String log("WIFI : AP Mode ssid will be "); + log += softAPSSID; + log += F(" with address "); + log += WiFi.softAPIP().toString(); + addLog(LOG_LEVEL_INFO, log); + } else { + String log("WIFI : Error while starting AP Mode with SSID: "); + log += softAPSSID; + log += F(" IP: "); + log += apIP.toString(); + addLog(LOG_LEVEL_ERROR, log); + } + #ifdef ESP32 + + #else + if(wifi_softap_dhcps_status() != DHCP_STARTED) { + if(!wifi_softap_dhcps_start()) { + addLog(LOG_LEVEL_ERROR, "WIFI : [AP] wifi_softap_dhcps_start failed!"); + } + } + #endif + } else { + timerAPoff = 0; // Disable timer to switch AP off + if (dnsServerActive) { + dnsServerActive = false; + dnsServer.stop(); + } + } } -//******************************************************************************** -// Set Wifi AP Mode -//******************************************************************************** + void setWifiMode(WiFiMode_t wifimode) { + if (WiFi.getMode() == wifimode) { + return; + } switch (wifimode) { case WIFI_OFF: addLog(LOG_LEVEL_INFO, F("WIFI : Switch off WiFi")); @@ -386,95 +380,9 @@ void setWifiMode(WiFiMode_t wifimode) { } setUseStaticIP(useStaticIP()); WiFi.mode(wifimode); + delay(30); // Must allow for some time to init. } -void changeWifiMode(WiFiMode_t wifimode) -{ - if (wifimode == WiFi.getMode()) return; - if (WiFi.getMode() == WIFI_OFF) { - // Any mode can be selected, starting from off mode. - addLog(LOG_LEVEL_INFO, F("WIFI : Switch on WiFi")); - setWifiMode(wifimode); - return; - } - switch (wifimode) { - case WIFI_OFF: - WifiDisconnect(); - setWifiMode(WIFI_OFF); - return; - case WIFI_STA: - if (WifiIsAP(WiFi.getMode())) { - // Change from AP mode to STA mode, must disconnect clients. - dnsServerActive = false; - dnsServer.stop(); - if (WiFi.softAPdisconnect(true)) { - String log("WIFI : AP Mode Disabled for SSID: "); - log += WifiGetAPssid(); - addLog(LOG_LEVEL_INFO, log); - } else { - String log("WIFI : Error while disabling AP Mode with SSID: "); - log += WifiGetAPssid(); - addLog(LOG_LEVEL_ERROR, log); - } - } - setWifiMode(wifimode); - WiFi.reconnect(); - break; - case WIFI_AP_STA: - case WIFI_AP: - { - if (WifiIsSTA(WiFi.getMode())) { - // Should disable WiFi first and then restart in AP mode. - WifiDisconnect(); - if (wifimode == WIFI_AP_STA) { - WiFi.reconnect(); - } -// setWifiMode(WIFI_OFF); - delay(100); - } - // create and store unique AP SSID/PW to prevent ESP from starting AP mode with default SSID and No password! - // setup ssid for AP Mode when needed - setWifiMode(wifimode); - WiFi.setAutoConnect(false); - String softAPSSID=WifiGetAPssid(); - String pwd = SecuritySettings.WifiAPKey; - IPAddress subnet(DEFAULT_AP_SUBNET); - if (!WiFi.softAPConfig(apIP, apIP, subnet)) { - addLog(LOG_LEVEL_ERROR, "WIFI : [AP] softAPConfig failed!"); - } - if (WiFi.softAP(softAPSSID.c_str(),pwd.c_str())) { - String log("WIFI : AP Mode ssid will be "); - log += softAPSSID; - log += F(" with address "); - log += WiFi.softAPIP().toString(); - addLog(LOG_LEVEL_INFO, log); - } else { - String log("WIFI : Error while starting AP Mode with SSID: "); - log += softAPSSID; - log += F(" IP: "); - log += apIP.toString(); - addLog(LOG_LEVEL_ERROR, log); - } - #ifdef ESP32 - - #else - if(wifi_softap_dhcps_status() != DHCP_STARTED) { - if(!wifi_softap_dhcps_start()) { - addLog(LOG_LEVEL_ERROR, "WIFI : [AP] wifi_softap_dhcps_start failed!"); - } - } - #endif - // Start DNS, only used if the ESP has no valid WiFi config - // It will reply with it's own address on all DNS requests - // (captive portal concept) - dnsServerActive = true; - dnsServer.start(DNS_PORT, "*", apIP); - break; - } - default: - break; - } -} //******************************************************************************** // Determine Wifi AP name to set. (also used for mDNS) @@ -503,6 +411,25 @@ bool useStaticIP() { return (Settings.IP[0] != 0 && Settings.IP[0] != 255); } +bool WiFiConnected() { + // For ESP82xx, do not rely on WiFi.status() with event based wifi. + return wifiStatus == ESPEASY_WIFI_SERVICES_INITIALIZED; +} + +void WiFiConnectRelaxed() { + if (WiFiConnected()) + return; //already connected, need to disconnect first + if (prepareWiFi()) { + if (selectValidWiFiSettings()) { + tryConnectWiFi(); + return; + } + } + addLog(LOG_LEVEL_ERROR, F("WIFI : Could not connect to AP!")); + //everything failed, activate AP mode (will deactivate automatically after a while if its connected again) + setAP(true); +} + //******************************************************************************** // Set Wifi config //******************************************************************************** @@ -511,6 +438,7 @@ bool prepareWiFi() { addLog(LOG_LEVEL_ERROR, F("WIFI : No valid wifi settings")); return false; } + setSTA(true); String log = ""; char hostname[40]; strncpy(hostname, WifiGetHostname().c_str(), sizeof(hostname)); @@ -583,13 +511,33 @@ bool wifiConnectTimeoutReached() { return timeOutReached(last_wifi_connect_attempt_moment + DEFAULT_WIFI_CONNECTION_TIMEOUT + randomOffset_in_sec); } +void setupStaticIPconfig() { + setUseStaticIP(useStaticIP()); + if (!useStaticIP()) return; + const IPAddress ip = Settings.IP; + const IPAddress gw = Settings.Gateway; + const IPAddress subnet = Settings.Subnet; + const IPAddress dns = Settings.DNS; + String log = F("IP : Static IP : "); + log += formatIP(ip); + log += F(" GW: "); + log += formatIP(gw); + log += F(" SN: "); + log += formatIP(subnet); + log += F(" DNS: "); + log += formatIP(dns); + addLog(LOG_LEVEL_INFO, log); + WiFi.config(ip, gw, subnet, dns); +} + //******************************************************************************** // Simply start the WiFi connection sequence //******************************************************************************** bool tryConnectWiFi() { -// if (wifiSetup && !wifiSetupConnect) -// return false; - if (processedTryConnect) return false; + if (wifiSetupConnect) { + lastWiFiSettings = 0; // Force to load the first settings. + wifi_connect_attempt = 0; + } if (wifiStatus != ESPEASY_WIFI_DISCONNECTED) { if (!WifiIsAP(WiFi.getMode())) { // Only when not in AP mode. @@ -598,12 +546,16 @@ bool tryConnectWiFi() { } if (!wifiConnectTimeoutReached()) return true; // timeout not reached yet, thus no need to retry again. - setWebserverRunning(false); if (!selectValidWiFiSettings()) { addLog(LOG_LEVEL_ERROR, F("WIFI : No valid WiFi settings!")); return false; } - + if (wifi_connect_attempt != 0 && (wifi_connect_attempt % 2) == 0) { + selectNextWiFiSettings(); + } + if (wifi_connect_attempt > 5) { + setAP(true); + } const char* ssid = getLastWiFiSettingsSSID(); const char* passphrase = getLastWiFiSettingsPassphrase(); String log = F("WIFI : Connecting "); @@ -611,26 +563,8 @@ bool tryConnectWiFi() { log += F(" attempt #"); log += wifi_connect_attempt; addLog(LOG_LEVEL_INFO, log); - + setupStaticIPconfig(); last_wifi_connect_attempt_moment = millis(); - if (useStaticIP()) - { - const IPAddress ip = Settings.IP; - const IPAddress gw = Settings.Gateway; - const IPAddress subnet = Settings.Subnet; - const IPAddress dns = Settings.DNS; - log = F("IP : Static IP : "); - log += formatIP(ip); - log += F(" GW: "); - log += formatIP(gw); - log += F(" SN: "); - log += formatIP(subnet); - log += F(" DNS: "); - log += formatIP(dns); - addLog(LOG_LEVEL_INFO, log); - WiFi.config(ip, gw, subnet, dns); - markGotIP(); - } switch (wifi_connect_attempt) { case 0: if (lastBSSID[0] == 0) @@ -642,6 +576,7 @@ bool tryConnectWiFi() { WiFi.begin(ssid, passphrase); } ++wifi_connect_attempt; + logConnectionStatus(); switch (WiFi.status()) { case WL_NO_SSID_AVAIL: { log = F("WIFI : No SSID found matching: "); @@ -658,7 +593,6 @@ bool tryConnectWiFi() { default: break; } - processedTryConnect = true; return true; // Sent } @@ -736,6 +670,67 @@ String formatScanResult(int i, const String& separator) { return result; } +#ifndef ESP32 +String SDKwifiStatusToString(uint8_t sdk_wifistatus) { + switch (sdk_wifistatus) { + case STATION_IDLE: return F("STATION_IDLE"); + case STATION_CONNECTING: return F("STATION_CONNECTING"); + case STATION_WRONG_PASSWORD: return F("STATION_WRONG_PASSWORD"); + case STATION_NO_AP_FOUND: return F("STATION_NO_AP_FOUND"); + case STATION_CONNECT_FAIL: return F("STATION_CONNECT_FAIL"); + case STATION_GOT_IP: return F("STATION_GOT_IP"); + } + return F("Unknown"); +} +#endif + +String ArduinoWifiStatusToString(uint8_t arduino_corelib_wifistatus) { + String log; + switch (arduino_corelib_wifistatus) { + case WL_IDLE_STATUS: log += F("WL_IDLE_STATUS"); break; + case WL_NO_SSID_AVAIL: log += F("WL_NO_SSID_AVAIL"); break; + case WL_SCAN_COMPLETED: log += F("WL_SCAN_COMPLETED"); break; + case WL_CONNECTED: log += F("WL_CONNECTED"); break; + case WL_CONNECT_FAILED: log += F("WL_CONNECT_FAILED"); break; + case WL_CONNECTION_LOST: log += F("WL_CONNECTION_LOST"); break; + case WL_DISCONNECTED: log += F("WL_DISCONNECTED"); break; + default: log += arduino_corelib_wifistatus; break; + } + return log; +} + +String ESPeasyWifiStatusToString() { + String log; + switch (wifiStatus) { + case ESPEASY_WIFI_DISCONNECTED: log += F("ESPEASY_WIFI_DISCONNECTED"); break; + case ESPEASY_WIFI_CONNECTED: log += F("ESPEASY_WIFI_CONNECTED"); break; + case ESPEASY_WIFI_GOT_IP: log += F("ESPEASY_WIFI_GOT_IP"); break; + case ESPEASY_WIFI_SERVICES_INITIALIZED: log += F("ESPEASY_WIFI_SERVICES_INITIALIZED"); break; + default: log += wifiStatus; + } + return log; +} + +void logConnectionStatus() { + const uint8_t arduino_corelib_wifistatus = WiFi.status(); + String log; + #ifndef ESP32 + const uint8_t sdk_wifistatus = wifi_station_get_connect_status(); + if ((arduino_corelib_wifistatus == WL_CONNECTED) != (sdk_wifistatus == STATION_GOT_IP)) { + log = F("WIFI : SDK station status differs from Arduino status. SDK-status: "); + log += SDKwifiStatusToString(sdk_wifistatus); + log += F(" Arduino status: "); + log += ArduinoWifiStatusToString(arduino_corelib_wifistatus); + addLog(LOG_LEVEL_ERROR, log); + } + #endif + log = F("WIFI : Arduino wifi status: "); + log += ArduinoWifiStatusToString(arduino_corelib_wifistatus); + log += F(" ESPeasy internal wifi status: "); + log += ESPeasyWifiStatusToString(); + addLog(LOG_LEVEL_DEBUG_MORE, log); +} + //******************************************************************************** // Check if we are still connected to a Wifi AP @@ -745,18 +740,12 @@ void WifiCheck() if(wifiSetup) return; - if (WifiIsAP(WiFi.getMode())) { - // disable AP after timeout. - if (timerAPoff && timeOutReached(timerAPoff)) { - setWifiState(WifiDisableAP); - } + processDisableAPmode(); + if (wifiStatus != ESPEASY_WIFI_SERVICES_INITIALIZED) { + WiFiConnectRelaxed(); } - - if (!processedTryConnect) { - // By running the connect attempts from this function (ran every second) - // the retry interval is at a slower pace. Some accesspoints do not react - // very well to retry attempts at msec intervals - tryConnectWiFi(); + if (mqtt_reconnect_count > 10) { + connectionCheckHandler(); } } diff --git a/src/Misc.ino b/src/Misc.ino index ab49516600..a1a0804eda 100644 --- a/src/Misc.ino +++ b/src/Misc.ino @@ -793,6 +793,7 @@ String LoadSettings() else{ addLog(LOG_LEVEL_ERROR, F("CRC : SecuritySettings CRC ...FAIL")); } + setUseStaticIP(useStaticIP()); return(err); } @@ -1252,6 +1253,78 @@ unsigned long FreeMem(void) #endif } +/********************************************************************************************\ + Get system information + \*********************************************************************************************/ +String getLastBootCauseString() { + switch (lastBootCause) + { + case BOOT_CAUSE_MANUAL_REBOOT: return F("Manual reboot"); + case BOOT_CAUSE_DEEP_SLEEP: //nobody should ever see this, since it should sleep again right away. + return F("Deep sleep"); + case BOOT_CAUSE_COLD_BOOT: + return F("Cold boot"); + case BOOT_CAUSE_EXT_WD: + return F("External Watchdog"); + } + return F("Unknown"); +} + +String getSystemBuildString() { + String result; + result += BUILD; + result += F(" "); + result += F(BUILD_NOTES); + return result; +} + +String getPluginDescriptionString() { + String result; + #ifdef PLUGIN_BUILD_NORMAL + result += F(" [Normal]"); + #endif + #ifdef PLUGIN_BUILD_TESTING + result += F(" [Testing]"); + #endif + #ifdef PLUGIN_BUILD_DEV + result += F(" [Development]"); + #endif + return result; +} + +String getSystemLibraryString() { + String result; + #if defined(ESP32) + result += F("ESP32 SDK "); + result += ESP.getSdkVersion(); + #else + result += F("ESP82xx Core "); + result += ESP.getCoreVersion(); + result += F(", NONOS SDK "); + result += system_get_sdk_version(); + result += F(", LWIP: "); + result += getLWIPversion(); + #endif + return result; +} + +#ifndef ESP32 +String getLWIPversion() { + String result; + result += LWIP_VERSION_MAJOR; + result += F("."); + result += LWIP_VERSION_MINOR; + result += F("."); + result += LWIP_VERSION_REVISION; + if (LWIP_VERSION_IS_RC) { + result += F("-RC"); + result += LWIP_VERSION_RC; + } else if (LWIP_VERSION_IS_DEVELOPMENT) { + result += F("-dev"); + } + return result; +} +#endif /********************************************************************************************\ Check if string is valid float diff --git a/src/Modbus.ino b/src/Modbus.ino new file mode 100644 index 0000000000..fe64b066ef --- /dev/null +++ b/src/Modbus.ino @@ -0,0 +1,224 @@ + +# ifndef MODBUS_H +# define MODBUS_H + +enum MODBUS_states_t {MODBUS_IDLE, MODBUS_RECEIVE, MODBUS_RECEIVE_PAYLOAD}; +enum MODBUS_registerTypes_t {signed16, unsigned16, signed32, unsigned32, signed64, unsigned64}; + +#define MODBUS_FUNCTION_READ 4 + +class Modbus +{ + public: + Modbus(void); + bool handle(); + bool begin(uint8_t function, uint8_t ModbusID, uint16_t ModbusRegister, MODBUS_registerTypes_t type, char* IPaddress); + double read() { + if (resultReceived) { + resultReceived = false; + return result; + } + else + return -1; + }; + bool available() { + return resultReceived; + }; + unsigned int getReadErrors() { + return errcnt; + }; + void resetReadErrors() { + errcnt = 0; + }; + void stop() { + TXRXstate = MODBUS_IDLE; + handle(); + }; + bool tryRead (uint8_t ModbusID, uint16_t M_register, MODBUS_registerTypes_t type, char* IPaddress, double &result); + + private: + WiFiClient *ModbusClient; // pointer to tcp client + unsigned int errcnt; + char sendBuffer[12] = {0, 1, 0, 0, 0, 6, 0x7e, 4, 0x9d, 7, 0, 1}; + String LogString = ""; // for debug logging + unsigned long timeout; // send and read timeout + MODBUS_states_t TXRXstate;// state for handle() state machine + unsigned int RXavailable; + unsigned int payLoad; // number of bytes to receive as payload. Payload may come as seperate frame. + bool hasTimeout(); + MODBUS_registerTypes_t incomingValue; // how to interpret the incoming value + double result; // incoming value, converted to double + bool resultReceived; // incoming value is valid ? + bool isBusy(void) { + return TXRXstate != MODBUS_IDLE; + }; + uint16_t currentRegister; + uint8_t currentFunction; +}; +#endif + + + +Modbus::Modbus() : ModbusClient(nullptr), errcnt(0), timeout(0), + TXRXstate(MODBUS_IDLE), RXavailable(0), payLoad(0) {} + +bool Modbus::begin(uint8_t function, uint8_t ModbusID, uint16_t ModbusRegister, MODBUS_registerTypes_t type, char* IPaddress) +{ + currentRegister = ModbusRegister; + currentFunction = function; + incomingValue = type; + resultReceived = false; + ModbusClient = new WiFiClient(); + ModbusClient->setNoDelay(true); + ModbusClient->setTimeout(200); + timeout = millis(); + ModbusClient->flush(); + + if (ModbusClient->connected()) { + LogString += F(" already connected. "); + } else { + LogString += F("connect: "); LogString += IPaddress; + if ( !ModbusClient->connect(IPaddress, 502)) { + LogString += F(" fail. "); + TXRXstate = MODBUS_IDLE; + errcnt++; + if (LogString.length() > 1 ) addLog(LOG_LEVEL_DEBUG, LogString); + return false; + } + } + LogString += F(" OK, sending read request: "); + + sendBuffer[6] = ModbusID ; + sendBuffer[7] = function; + sendBuffer[8] = (ModbusRegister >> 8) ; + sendBuffer[9] = (ModbusRegister & 0x00ff) ; + if ((incomingValue == signed64) || (incomingValue == unsigned64)) + sendBuffer[11] = 4; + if ((incomingValue == signed32) || (incomingValue == unsigned32)) + sendBuffer[11] = 2; + if ((incomingValue == signed16) || (incomingValue == unsigned16)) + sendBuffer[11] = 1; + ModbusClient->flush(); + ModbusClient->write(&sendBuffer[0], sizeof(sendBuffer)); + for (unsigned int i = 0; i < sizeof(sendBuffer); i++) { + LogString += ((unsigned int)(sendBuffer[i])); + LogString += (" "); + } + TXRXstate = MODBUS_RECEIVE; + if (LogString.length() > 1 ) addLog(LOG_LEVEL_DEBUG, LogString); + return true; +} + +bool Modbus::handle() { + unsigned int RXavailable = 0; + LogString = ""; + int64_t rxValue = 0; + switch ( TXRXstate ) { + + case MODBUS_IDLE: + // clean up; + if (ModbusClient) { + ModbusClient->flush(); + ModbusClient->stop(); + delete (ModbusClient); + delay(1); + ModbusClient = nullptr; + } + break; + + case MODBUS_RECEIVE: + if (hasTimeout()) break; + if (ModbusClient->available() < 9) break; + + LogString += F("reading bytes: "); + for (int a = 0; a < 9; a++) { + payLoad = ModbusClient->read(); + LogString += (payLoad); LogString += F(" "); + } + LogString += F("> "); + if (payLoad > 8) { + LogString += "Payload too large !? "; + errcnt++; + TXRXstate = MODBUS_IDLE; + } + + case MODBUS_RECEIVE_PAYLOAD: + if (hasTimeout()) break; + RXavailable = ModbusClient->available(); + if (payLoad != RXavailable) { + TXRXstate = MODBUS_RECEIVE_PAYLOAD; + break; + } + for (unsigned int i = 0; i < RXavailable; i++) { + rxValue = rxValue << 8; + char a = ModbusClient->read(); + rxValue = rxValue | a; + LogString += ((int)a); LogString += (" "); + } + switch (incomingValue) { + case signed16: + result = (int16_t) rxValue; + break; + case unsigned16: + result = (uint16_t) rxValue; + break; + case signed32: + result = (int32_t) rxValue; + break; + case unsigned32: + result = (uint32_t) rxValue; + break; + case signed64: + result = (int64_t) rxValue; + break; + case unsigned64: + result = (uint64_t) rxValue; + break; + } + + LogString += "value: "; LogString += result; + //if ((Settings.UseNTP) && (hour() == 0)) errcnt = 0; + + TXRXstate = MODBUS_IDLE; + + resultReceived = true; + break; + + default: + LogString += F("default. "); + TXRXstate = MODBUS_IDLE; + break; + + } + if (LogString.length() > 1 ) addLog(LOG_LEVEL_DEBUG, LogString); + return true; +} + +bool Modbus::hasTimeout() +{ + if ( (millis() - timeout) > 10000) { // too many bytes or timeout + LogString += F("Modbus RX timeout. "); LogString += String(ModbusClient->available()); + errcnt++; + TXRXstate = MODBUS_IDLE; + return true; + } + return false; +} + + + +// tryread can be called in a round robin fashion. It will initiate a read if Modbus is idle and update the result once it is available. +// subsequent calls (if Modbus is busy etc. ) will return false and not update the result. +// Use to read multiple values non blocking in an re-entrant function. Not tested yet. +bool Modbus::tryRead (uint8_t ModbusID, uint16_t M_register, MODBUS_registerTypes_t type, char* IPaddress, double &result) { + if (isBusy()) return false; // not done yet + if (available()) { + if ((currentFunction == MODBUS_FUNCTION_READ ) && (currentRegister == M_register)) { + result = read(); // result belongs to this request. + return true; + } + } else { + begin(MODBUS_FUNCTION_READ, ModbusID, M_register, type, IPaddress); // idle and no result -> begin read request + } + return false; +} diff --git a/src/Networking.ino b/src/Networking.ino index 71ae7fd670..d355d3a873 100644 --- a/src/Networking.ino +++ b/src/Networking.ino @@ -2,9 +2,9 @@ // Syslog // UDP system messaging // SSDP - #if LWIP_VERSION_MAJOR == 2 - #define IP2STR(addr) (uint8_t)((uint32_t)addr & 0xFF), (uint8_t)(((uint32_t)addr >> 8) & 0xFF), (uint8_t)(((uint32_t)addr >> 16) & 0xFF), (uint8_t)(((uint32_t)addr >> 24) & 0xFF) - #endif +// #if LWIP_VERSION_MAJOR == 2 +#define IPADDR2STR(addr) (uint8_t)((uint32_t)addr & 0xFF), (uint8_t)(((uint32_t)addr >> 8) & 0xFF), (uint8_t)(((uint32_t)addr >> 16) & 0xFF), (uint8_t)(((uint32_t)addr >> 24) & 0xFF) +// #endif /*********************************************************************************************\ @@ -442,7 +442,7 @@ void SSDP_send(byte method) { SSDP_INTERVAL, Settings.Build, uuid, - IP2STR(&ip) + IPADDR2STR(&ip) ); _server->append(buffer, len); @@ -607,7 +607,7 @@ bool WiFiConnected(uint32_t timeout_ms) { // Apparently something needs network, perform check to see if it is ready now. // if (!tryConnectWiFi()) // return false; - while (wifiStatus != ESPEASY_WIFI_SERVICES_INITIALIZED) { + while (!WiFiConnected()) { if (timeOutReached(timer)) { return false; } @@ -617,6 +617,7 @@ bool WiFiConnected(uint32_t timeout_ms) { } bool hostReachable(const IPAddress& ip) { + if (!WiFiConnected()) return false; // Only do 1 ping at a time to return early byte retry = 3; while (retry > 0) { @@ -634,15 +635,15 @@ bool hostReachable(const IPAddress& ip) { addLog(LOG_LEVEL_ERROR, log); if (ip[1] == 0 && ip[2] == 0 && ip[3] == 0) { // Work-around to fix connected but not able to communicate. - addLog(LOG_LEVEL_ERROR, F("Wifi : Detected strange behavior, reset wifi.")); - setWifiState(WifiOff); - delay(100); - setWifiState(WifiTryConnect); + addLog(LOG_LEVEL_ERROR, F("Wifi : Detected strange behavior, reconnect wifi.")); + WifiDisconnect(); } + logConnectionStatus(); return false; } bool hostReachable(const String& hostname) { + if (!WiFiConnected()) return false; IPAddress remote_addr; if (WiFi.hostByName(hostname.c_str(), remote_addr)) { return hostReachable(remote_addr); diff --git a/src/StringConverter.ino b/src/StringConverter.ino index f4cab9e8b5..74d1b54f91 100644 --- a/src/StringConverter.ino +++ b/src/StringConverter.ino @@ -283,6 +283,8 @@ void parseSystemVariables(String& s, boolean useURLencode) #endif repl(F("%CR%"), F("\r"), s, useURLencode); repl(F("%LF%"), F("\n"), s, useURLencode); + repl(F("%SP%"), F(" "), s, useURLencode); //space + SMART_REPL(F("%ip4%"),WiFi.localIP().toString().substring(WiFi.localIP().toString().lastIndexOf('.')+1)) //4th IP octet SMART_REPL(F("%ip%"),WiFi.localIP().toString()) SMART_REPL(F("%rssi%"), String((wifiStatus == ESPEASY_WIFI_DISCONNECTED) ? 0 : WiFi.RSSI())) SMART_REPL(F("%ssid%"), (wifiStatus == ESPEASY_WIFI_DISCONNECTED) ? F("--") : WiFi.SSID()) diff --git a/src/WebServer.ino b/src/WebServer.ino index c8ff4ccff2..88d63c127d 100644 --- a/src/WebServer.ino +++ b/src/WebServer.ino @@ -529,6 +529,7 @@ void WebServerInit() WebServer.on("/devices", handle_devices); WebServer.on("/notifications", handle_notifications); WebServer.on("/log", handle_log); + WebServer.on("/logjson", handle_log_JSON); WebServer.on("/tools", handle_tools); WebServer.on("/i2cscanner", handle_i2cscanner); WebServer.on("/wifiscanner", handle_wifiscanner); @@ -640,6 +641,22 @@ void getWebPageTemplateDefault(const String& tmplName, String& tmpl) "" ); } + else if (tmplName == F("TmplDsh")) + { + tmpl += F( + "" + "" + "" + "{{name}}" + "" + "{{js}}" + "{{css}}" + "" + "" + "{{content}}" + "" + ); + } else //all other template names e.g. TmplStd { tmpl += F( @@ -2735,12 +2752,14 @@ void addSubmitButton(const String &value, const String &name) } // add copy to clipboard button -void addCopyButton(const String &value, const String &name) +void addCopyButton(const String &value, const String &delimiter, const String &name) { TXBuffer += F(""); TXBuffer += F("