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)
"
"
+ "{{content}}"
+ ""
);
}
+ else if (tmplName == F("TmplDsh"))
+ {
+ tmpl += F(
+ ""
+ "