diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino
index 245cc5edb..98378f368 100644
--- a/sonoff/_changelog.ino
+++ b/sonoff/_changelog.ino
@@ -1,4 +1,7 @@
-/* 6.4.1.19 20190222
+/* 6.4.1.20 20190304
+ * Changed webserver content handling from single String to small Chunks increasing RAM
+ *
+ * 6.4.1.19 20190222
* Add command SetOption37 for RGBCW color mapping (#5326)
* Add Korean language translations (#5344)
* Fix Energy TotalStartTime when commands EnergyReset0 and/or EnergyReset3 used (#5373)
diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h
index d4bfa230b..6dc3c6f38 100644
--- a/sonoff/sonoff_version.h
+++ b/sonoff/sonoff_version.h
@@ -20,7 +20,7 @@
#ifndef _SONOFF_VERSION_H_
#define _SONOFF_VERSION_H_
-#define VERSION 0x06040113
+#define VERSION 0x06040114
#define D_PROGRAMNAME "Sonoff-Tasmota"
#define D_AUTHOR "Theo Arends"
diff --git a/sonoff/support.ino b/sonoff/support.ino
index 8b3cbe58a..67f53d451 100644
--- a/sonoff/support.ino
+++ b/sonoff/support.ino
@@ -1279,6 +1279,17 @@ void AddLog_P(uint8_t loglevel, const char *formatP, const char *formatP2)
AddLog(loglevel);
}
+void AddLog_P2(uint8_t loglevel, PGM_P formatP, ...)
+{
+ // This uses char strings. Be aware of sending %% if % is needed
+ va_list arg;
+ va_start(arg, formatP);
+ int len = vsnprintf_P(log_data, sizeof(log_data), formatP, arg);
+ va_end(arg);
+
+ AddLog(loglevel);
+}
+
void AddLogBuffer(uint8_t loglevel, uint8_t *buffer, int count)
{
snprintf_P(log_data, sizeof(log_data), PSTR("DMP:"));
diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino
index cbd52ccee..8df9529b4 100644
--- a/sonoff/xdrv_01_webserver.ino
+++ b/sonoff/xdrv_01_webserver.ino
@@ -27,6 +27,8 @@
#define XDRV_01 1
+#define CHUNKED_BUFFER_SIZE 400 // Chunk buffer size
+
#ifndef WIFI_SOFT_AP_CHANNEL
#define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 11 as used by SmartConfig web GUI
#endif
@@ -49,7 +51,7 @@ const char HTTP_HEAD[] PROGMEM =
"
"
""
""
- "{h} - {v}"
+ "%s - %s"
"";
+ "window.onload=u;";
const char HTTP_SCRIPT_ROOT[] PROGMEM =
"function la(p){"
@@ -76,17 +77,17 @@ const char HTTP_SCRIPT_ROOT[] PROGMEM =
"a=p;"
"clearTimeout(lt);"
"}"
- "if(x!=null){x.abort();}" // Abort if no response within Settings.web_refresh milliseconds (happens on restart 1)
+ "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1)
"x=new XMLHttpRequest();"
"x.onreadystatechange=function(){"
"if(x.readyState==4&&x.status==200){"
- "var s=x.responseText.replace(/{t}/g,\"\").replace(/{s}/g,\"\").replace(/{m}/g,\" | \").replace(/{e}/g,\" |
\").replace(/{c}/g,\"%'>\").replace(/{s}/g,\"
\").replace(/{m}/g,\" | \").replace(/{e}/g,\" |
---|
\").replace(/{c}/g,\"%%'>
hasArg("m")
"x.send();"
- "lt=setTimeout(la,{a});" // Settings.web_refresh
+ "lt=setTimeout(la,%d);" // Settings.web_refresh
"}"
"function lb(p){"
"la('&d='+p);" // &d related to WebGetArg("d", tmp, sizeof(tmp));
@@ -129,7 +130,7 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM =
"x.onreadystatechange=function(){"
"if(x.readyState==4&&x.status==200){"
"var z,d;"
- "d=x.responseText.split(/\1/);" // Field separator
+ "d=x.responseText.split(/}1/);" // Field separator
"id=d.shift();"
"if(d.shift()==0){t.value='';}"
"z=d.shift();"
@@ -141,18 +142,18 @@ const char HTTP_SCRIPT_CONSOL[] PROGMEM =
"x.open('GET','cs?c2='+id+o,true);" // Related to WebServer->hasArg("c2") and WebGetArg("c2", stmp, sizeof(stmp))
"x.send();"
"}"
- "lt=setTimeout(l,{a});"
+ "lt=setTimeout(l,%d);"
"return false;"
"}"
"window.onload=l;";
const char HTTP_MODULE_TEMPLATE_REPLACE[] PROGMEM =
- "\2%d'>%s (%d\3"; // \2 and \3 are used in below os.replace
+ "}2%d'>%s (%d}3"; // }2 and }3 are used in below os.replace
const char HTTP_SCRIPT_MODULE_TEMPLATE[] PROGMEM =
"var os;"
"function sk(s,g){" // s = value, g = id and name
- "var o=os.replace(/\2/g,\"