3.9.21 20170224
* Add ajax to web root page and web console (#79)
* Add commands SwitchMode1..4 and enable user switches 2, 3 and 4 (#84,
#88)
* Fix MQTT upgrade when webserver is active
This commit is contained in:
arendst 2017-02-24 18:17:48 +01:00
parent 3f73e48bfa
commit 3fefb9b931
8 changed files with 193 additions and 124 deletions

View File

@ -1,7 +1,7 @@
## Sonoff-Tasmota
Provide ESP8266 based Sonoff by [iTead Studio](https://www.itead.cc/) and ElectroDragon IoT Relay with Serial, Web and MQTT control allowing 'Over the Air' or OTA firmware updates using Arduino IDE.
Current version is **3.9.20** - See ```sonoff/_releasenotes.ino``` for change information.
Current version is **3.9.21** - See ```sonoff/_releasenotes.ino``` for change information.
- This version provides all (Sonoff) modules in one file and starts up with Sonoff Basic.
- Once uploaded select module using the configuration webpage or the commands ```Modules``` and ```Module```.

Binary file not shown.

View File

@ -1,4 +1,9 @@
/* 3.9.20 20170221
/* 3.9.21 20170224
* Add ajax to web root page and web console (#79)
* Add commands SwitchMode1..4 and enable user switches 2, 3 and 4 (#84, #88)
* Fix MQTT upgrade when webserver is active
*
* 3.9.20 20170221
* Add minimal basic authentication to Web Admin mode (#87)
* Fix Hue and add HSB support (#89)
*

View File

@ -114,7 +114,7 @@ struct SYSCFG {
uint8_t power;
uint8_t ledstate;
uint8_t switchmode;
uint8_t ex_switchmode; // Not used since 3.9.21
char domoticz_in_topic[33];
char domoticz_out_topic[33];
@ -185,5 +185,7 @@ struct SYSCFG {
uint8_t emulation;
char web_password[33];
uint8_t switchmode[4];
} sysCfg;

View File

@ -367,7 +367,7 @@ void CFG_DefaultSet2()
sysCfg.poweronstate = APP_POWERON_STATE;
sysCfg.pulsetime = APP_PULSETIME;
sysCfg.ledstate = APP_LEDSTATE;
sysCfg.switchmode = SWITCH_MODE;
// sysCfg.switchmode = SWITCH_MODE;
sysCfg.blinktime = APP_BLINKTIME;
sysCfg.blinkcount = APP_BLINKCOUNT;
sysCfg.sleep = APP_SLEEP;
@ -376,6 +376,7 @@ void CFG_DefaultSet2()
strlcpy(sysCfg.domoticz_out_topic, DOMOTICZ_OUT_TOPIC, sizeof(sysCfg.domoticz_out_topic));
sysCfg.domoticz_update_timer = DOMOTICZ_UPDATE_TIMER;
for (byte i = 0; i < 4; i++) {
sysCfg.switchmode[i] = SWITCH_MODE;
sysCfg.domoticz_relay_idx[i] = 0;
sysCfg.domoticz_key_idx[i] = 0;
sysCfg.domoticz_switch_idx[i] = 0;
@ -545,7 +546,7 @@ void CFG_Migrate_Part2()
sysCfg.hlw_kWhdoy = sysCfg2.hlw_kWhdoy;
}
if (sysCfg2.version >= 0x02001200) { // 2.0.18
sysCfg.switchmode = sysCfg2.switchmode;
sysCfg.switchmode[0] = sysCfg2.switchmode;
}
if (sysCfg2.version >= 0x02010000) { // 2.1.0
strlcpy(sysCfg.mqtt_fingerprint, sysCfg2.mqtt_fingerprint, sizeof(sysCfg.mqtt_fingerprint));
@ -629,6 +630,9 @@ void CFG_Delta()
if (sysCfg.version < 0x03091301) {
strlcpy(sysCfg.web_password, WEB_PASSWORD, sizeof(sysCfg.web_password));
}
if (sysCfg.version < 0x03091500) {
for (byte i = 0; i < 4; i++) sysCfg.switchmode[i] = sysCfg.ex_switchmode;
}
sysCfg.version = VERSION;
}

View File

@ -10,7 +10,7 @@
* ====================================================
*/
#define VERSION 0x03091400 // 3.9.20
#define VERSION 0x03091500 // 3.9.21
//#define BE_MINIMAL // Compile a minimal version if upgrade memory gets tight (still 404k)
// To be used as step 1. Next step is compile and use desired version
@ -118,7 +118,7 @@ enum emul_t {EMUL_NONE, EMUL_WEMO, EMUL_HUE, EMUL_MAX};
#define STATES 10 // loops per second
#define SYSLOG_TIMER 600 // Seconds to restore syslog_level
#define SERIALLOG_TIMER 600 // Seconds to disable SerialLog
#define OTA_ATTEMPTS 5 // Number of times to try fetching the new firmware
#define OTA_ATTEMPTS 10 // Number of times to try fetching the new firmware
#define INPUT_BUFFER_SIZE 100 // Max number of characters in serial buffer
#define TOPSZ 60 // Max number of characters in topic string
@ -213,12 +213,14 @@ int state = 0; // State per second flag
int mqttflag = 2; // MQTT connection messages flag
int otaflag = 0; // OTA state flag
int otaok = 0; // OTA result
byte otaretry = OTA_ATTEMPTS; // OTA retry counter
int restartflag = 0; // Sonoff restart flag
int wificheckflag = WIFI_RESTART; // Wifi state flag
int uptime = 0; // Current uptime in hours
int tele_period = 0; // Tele period timer
String Log[MAX_LOG_LINES]; // Web log buffer
byte logidx = 0; // Index in Web log buffer
byte logajaxflg = 0; // Reset web console log
byte Maxdevice = MAX_DEVICE; // Max number of devices supported
int status_update_timer = 0; // Refresh initial status
uint16_t pulse_timer = 0; // Power off timer
@ -891,11 +893,11 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
}
snprintf_P(svalue, sizeof(svalue), PSTR("{\"FriendlyName%d\":\"%s\"}"), index, sysCfg.friendlyname[index -1]);
}
else if (swt_flg && !strcmp(type,"SWITCHMODE")) {
else if (swt_flg && !strcmp(type,"SWITCHMODE") && (index > 0) && (index <= 4)) {
if ((data_len > 0) && (payload >= 0) && (payload < MAX_SWITCH_OPTION)) {
sysCfg.switchmode = payload;
sysCfg.switchmode[index -1] = payload;
}
snprintf_P(svalue, sizeof(svalue), PSTR("{\"SwitchMode\":%d}"), sysCfg.switchmode);
snprintf_P(svalue, sizeof(svalue), PSTR("{\"SwitchMode%d\":%d}"), index, sysCfg.switchmode[index-1]);
}
#ifdef USE_WEBSERVER
else if (!strcmp(type,"WEBSERVER")) {
@ -1184,7 +1186,7 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
snprintf_P(svalue, sizeof(svalue), PSTR("{\"Commands3\":\"%s%s, PulseTime, BlinkTime, BlinkCount"), (Maxdevice == 1) ? "Power, Light" : "Power1, Power2, Light1 Light2", (sysCfg.module != MOTOR) ? ", PowerOnState" : "");
#ifdef USE_WEBSERVER
snprintf_P(svalue, sizeof(svalue), PSTR("%s, Weblog, Webserver, Emulation"), svalue);
snprintf_P(svalue, sizeof(svalue), PSTR("%s, Weblog, Webserver, WebPassword, Emulation"), svalue);
#endif
if (swt_flg) snprintf_P(svalue, sizeof(svalue), PSTR("%s, SwitchMode"), svalue);
#ifdef USE_I2C
@ -1223,10 +1225,10 @@ void send_button_power(byte key, byte device, byte state)
char stopic[TOPSZ], svalue[TOPSZ], stemp1[10];
if (device > Maxdevice) device = 1;
if (!key && (device > Maxdevice)) device = 1;
snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), device);
snprintf_P(stopic, sizeof(stopic), PSTR("%s/%s/%s%s"),
SUB_PREFIX, (key) ? sysCfg.switch_topic : sysCfg.button_topic, sysCfg.mqtt_subtopic, (Maxdevice > 1) ? stemp1 : "");
SUB_PREFIX, (key) ? sysCfg.switch_topic : sysCfg.button_topic, sysCfg.mqtt_subtopic, (key || (Maxdevice > 1)) ? stemp1 : "");
if (state == 3) {
svalue[0] = '\0';
@ -1676,11 +1678,12 @@ void stateloop()
lastbutton[i] = button;
}
for (byte i = 0; i < Maxdevice; i++) if (pin[GPIO_SWT1 +i] < 99) {
// for (byte i = 0; i < Maxdevice; i++) if (pin[GPIO_SWT1 +i] < 99) {
for (byte i = 0; i < 4; i++) if (pin[GPIO_SWT1 +i] < 99) {
button = digitalRead(pin[GPIO_SWT1 +i]);
if (button != lastwallswitch[i]) {
switchflag = 3;
switch (sysCfg.switchmode) {
switch (sysCfg.switchmode[i]) {
case TOGGLE:
switchflag = 2; // Toggle
break;
@ -1698,9 +1701,9 @@ void stateloop()
}
if (switchflag < 3) {
if (sysCfg.mqtt_enabled && mqttClient.connected() && strcmp(sysCfg.switch_topic,"0")) {
send_button_power(1, i +1, switchflag); // Execute commend via MQTT
send_button_power(1, i +1, switchflag); // Execute commend via MQTT
} else {
do_cmnd_power(i +1, switchflag); // Execute command internally
do_cmnd_power(i +1, switchflag); // Execute command internally (if i < Maxdevice)
}
}
lastwallswitch[i] = button;
@ -1730,30 +1733,34 @@ void stateloop()
case (STATES/10)*2:
if (otaflag) {
otaflag--;
if (otaflag <= 0) {
otaflag = 12;
if (otaflag == 2){
otaretry = OTA_ATTEMPTS;
ESPhttpUpdate.rebootOnUpdate(false);
sl_blank(1);
// Try multiple times to get the update, in case we have a transient issue.
// e.g. Someone issued "cmnd/sonoffs/update 1" and all the devices
// are hammering the OTAURL.
for (byte i = 0; i < OTA_ATTEMPTS && !otaok; i++) {
// Delay an increasing pseudo-random time for each interation.
// Starting at 0 (no delay) up to a maximum of OTA_ATTEMPTS-1 seconds.
delay((ESP.getChipId() % 1000) * i);
}
if (otaflag <= 0) {
#ifdef USE_WEBSERVER
if (sysCfg.webserver) stopWebserver();
#endif // USE_WEBSERVER
otaflag = 92;
otaok = 0;
otaretry--;
if (otaretry) {
// snprintf_P(log, sizeof(log), PSTR("OTA: Attempt %d"), OTA_ATTEMPTS - otaretry);
// addLog(LOG_LEVEL_INFO, log);
otaok = (ESPhttpUpdate.update(sysCfg.otaUrl) == HTTP_UPDATE_OK);
if (!otaok) otaflag = 2;
}
}
if (otaflag == 10) { // Allow MQTT to reconnect
if (otaflag == 90) { // Allow MQTT to reconnect
otaflag = 0;
if (otaok) {
if ((sysCfg.module == SONOFF_TOUCH) || (sysCfg.module == SONOFF_4CH)) setFlashChipMode(1, 3); // DOUT - ESP8285
snprintf_P(svalue, sizeof(svalue), PSTR("Successful. Restarting"));
restartflag = 2;
} else {
sl_blank(0);
snprintf_P(svalue, sizeof(svalue), PSTR("Failed %s"), ESPhttpUpdate.getLastErrorString().c_str());
}
restartflag = 2; // Restart anyway to keep memory clean webserver
mqtt_publish_topic_P(0, PSTR("UPGRADE"), svalue);
}
}

View File

@ -14,6 +14,9 @@ enum upins_t {
GPIO_WS2812, // WS2812 Led string
GPIO_IRSEND, // IR remote
GPIO_SWT1, // User connected external switches
GPIO_SWT2,
GPIO_SWT3,
GPIO_SWT4,
GPIO_SENSOR_END };
// Text in webpage Module Parameters and commands GPIOS and GPIO
@ -27,14 +30,14 @@ const char sensors[GPIO_SENSOR_END][9] PROGMEM = {
"I2C SDA",
"WS2812",
"IRremote",
"Switch" };
"Switch1",
"Switch2",
"Switch3",
"Switch4" };
// Programmer selectable GPIO functionality offset by user selectable GPIOs
enum fpins_t {
GPIO_SWT2 = GPIO_SENSOR_END,
GPIO_SWT3,
GPIO_SWT4,
GPIO_KEY1, // Button usually connected to GPIO0
GPIO_KEY1 = GPIO_SENSOR_END, // Button usually connected to GPIO0
GPIO_KEY2,
GPIO_KEY3,
GPIO_KEY4,
@ -107,14 +110,22 @@ typedef struct MYTMPLT {
const mytmplt modules[MAXMODULE] PROGMEM = {
{ "Sonoff Basic", // Sonoff Basic
GPIO_KEY1, // GPIO00 Button
0, 0,
0, // GPIO01 Serial RXD
0, // GPIO02
GPIO_USER, // GPIO03 Serial TXD and Optional sensor
GPIO_USER, // GPIO04 Optional sensor
0, 0, 0, 0, 0, 0, 0,
0, // GPIO05
0, // GPIO06 (SD_CLK Flash)
0, // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
0, // GPIO08 (SD_DATA1 Flash QIO/DIO)
0, // GPIO09 (SD_DATA2 Flash QIO)
0, // GPIO10 (SD_DATA3 Flash QIO)
0, // GPIO11 (SD_CMD Flash)
GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off)
GPIO_USER, // GPIO14 Optional sensor
0, 0
0, // GPIO15
0 // GPIO16
},
{ "Sonoff RF", // Sonoff RF
GPIO_KEY1, // GPIO00 Button

View File

@ -53,34 +53,58 @@ const char HTTP_HEAD[] PROGMEM =
"document.getElementById('s1').value=l.innerText||l.textContent;"
"document.getElementById('p1').focus();"
"}"
"var sn=0;"
"var x=null;" // Allow for abortion
"var lt;" // Enable clearTimeout
"function la(p){"
"var a='';"
"if(la.arguments.length==1){"
"a='?o='+p;"
"clearTimeout(lt);"
"}"
"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){"
"document.getElementById('l1').innerHTML=x.responseText;"
"}"
"};"
"x.open('GET','ay'+a,true);"
"x.send();"
"lt=setTimeout(la,2000);"
"}"
"var sn=0;" // Scroll position
"var id=99;" // Get most of weblog initially
"function l(){"
"var e=document.getElementById('t1');"
"if(e.scrollTop>=sn){"
"var x=new XMLHttpRequest();"
"if(e.scrollTop>=sn){" // User scrolled back so no updates
"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 s1=x.responseText;"
"if(e.value.length==0){"
"e.value=s1;"
"}else{"
"var s2=e.value.slice(e.value.lastIndexOf(\"\\n\")+2);"
"var p2=s1.search(s2);"
"if(p2>-1){"
"e.value=e.value.replace(s2,s1.slice(p2));"
"}else{"
"e.value=e.value+\"\\n\"+s1;"
"}"
"}"
"var z,d;"
"d=x.responseXML;"
"id=d.getElementsByTagName('i')[0].childNodes[0].nodeValue;"
"if(d.getElementsByTagName('j')[0].childNodes[0].nodeValue==0){e.value=\"\";}"
"z=d.getElementsByTagName('l')[0].childNodes;"
"if(z.length>0){e.value+=z[0].nodeValue;}"
"e.scrollTop=99999;"
"sn=e.scrollTop;"
"}"
"};"
"x.open('GET','ax',true);"
"x.open('GET','ax?c2='+id,true);"
"x.send();"
"}"
"setTimeout(l,2000);"
"}"
"function lc(){"
"var e=document.getElementById('c1');"
// "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1)
"var x=new XMLHttpRequest();"
"x.open('GET','ax?c1='+e.value+'&c2='+id,true);"
"x.send();"
"e.value=\"\";"
"return false;"
"}"
"</script>"
"<style>"
"div,fieldset,input,select{padding:5px;font-size:1em;}"
@ -210,7 +234,7 @@ const char HTTP_FORM_UPG[] PROGMEM =
"<div id='f2' name='f2' style='display:none;text-align:center;'><b>Upload started ...</b></div>";
const char HTTP_FORM_CMND[] PROGMEM =
"<br/><textarea readonly id='t1' name='t1' cols='99' wrap='off'></textarea><br/><br/>"
"<form method='post' action='cs'>"
"<form method='get' onsubmit='return lc();'>"
"<input style='width:98%' id='c1' name='c1' length='99' placeholder='Enter command' autofocus><br/>"
// "<br/><button type='submit'>Send command</button>"
"</form>";
@ -261,6 +285,7 @@ void startWebserver(int type, IPAddress ipweb)
webServer->on("/cm", handleCmnd);
webServer->on("/cs", handleConsole);
webServer->on("/ax", handleAjax);
webServer->on("/ay", handleAjax2);
webServer->on("/in", handleInfo);
webServer->on("/rb", handleRestart);
webServer->on("/fwlink", handleRoot); // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
@ -276,6 +301,7 @@ void startWebserver(int type, IPAddress ipweb)
#endif // USE_EMULATION
webServer->onNotFound(handleNotFound);
}
logajaxflg = 0;
webServer->begin(); // Web server start
}
if (_httpflag != type) {
@ -356,73 +382,81 @@ void handleRoot()
if (_httpflag == HTTP_MANAGER) {
handleWifi0();
} else {
String page = FPSTR(HTTP_HEAD);
// page.replace("<meta", "<meta http-equiv=\"refresh\" content=\"4; URL=/\"><meta"); // Fails Edge (asks for reload)
// page.replace("</script>", "setTimeout(function(){window.location.reload(1);},4000);</script>"); // Repeats POST on All
page.replace("</script>", "setTimeout(function(){window.location.replace(\"/\");},4000);</script>"); // OK on All
page.replace("{v}", "Main menu");
page.replace("<body>", "<body onload='la()'>");
page += F("<div id='l1' name='l1'></div>");
if (Maxdevice) {
if (strlen(webServer->arg("o").c_str())) {
do_cmnd_power(atoi(webServer->arg("o").c_str()), 2);
}
page += F("<table style='width:100%'><tr>");
for (byte idx = 1; idx <= Maxdevice; idx++) {
page += F("<td style='width:");
page += String(100 / Maxdevice);
page += F("%'><form action='/?o=");
page += F("%'><button onclick='la(");
page += String(idx);
page += F("' method='post'><div style='text-align:center;font-weight:bold;font-size:");
page += String(70 - (Maxdevice * 8));
page += F("px'>");
page += (power & (0x01 << (idx -1))) ? "ON" : "OFF";
page += F("</div><br/><button>Toggle");
page += F(");'>Toggle");
if (Maxdevice > 1) {
page += F(" ");
page += String(idx);
}
page += F("</button></form></td>");
page += F("</button></td>");
}
page += F("</tr></table><br/>");
page += F("</tr></table>");
}
String tpage = "";
if (hlw_flg) tpage += hlw_webPresent();
#ifdef USE_DS18B20
if (pin[GPIO_DSB] < 99) tpage += dsb_webPresent();
#endif // USE_DS18B20
#ifdef USE_DS18x20
if (pin[GPIO_DSB] < 99) tpage += ds18x20_webPresent();
#endif // USE_DS18x20
#ifdef USE_DHT
if (dht_type) tpage += dht_webPresent();
#endif // USE_DHT
#ifdef USE_I2C
if (i2c_flg) {
tpage += htu_webPresent();
tpage += bmp_webPresent();
tpage += bh1750_webPresent();
}
#endif // USE_I2C
if (tpage.length() > 0) {
page += F("<table style='width:100%'>");
page += tpage;
page += F("</table><br/>");
}
if (_httpflag == HTTP_ADMIN) {
page += FPSTR(HTTP_BTN_MENU1);
page += FPSTR(HTTP_BTN_RSTRT);
}
showPage(page);
}
}
void handleAjax2()
{
if (strlen(webServer->arg("o").c_str())) do_cmnd_power(atoi(webServer->arg("o").c_str()), 2);
String tpage = "";
if (hlw_flg) tpage += hlw_webPresent();
#ifdef USE_DS18B20
if (pin[GPIO_DSB] < 99) tpage += dsb_webPresent();
#endif // USE_DS18B20
#ifdef USE_DS18x20
if (pin[GPIO_DSB] < 99) {
tpage += ds18x20_webPresent();
ds18x20_search(); // Check for changes in sensors number
ds18x20_convert(); // Start Conversion, takes up to one second
#endif // USE_DS18x20
}
#endif // USE_DS18x20
#ifdef USE_DHT
if (dht_type) tpage += dht_webPresent();
#endif // USE_DHT
#ifdef USE_I2C
if (i2c_flg) {
tpage += htu_webPresent();
tpage += bmp_webPresent();
tpage += bh1750_webPresent();
}
#endif // USE_I2C
String page = "";
if (tpage.length() > 0) {
page += F("<table style='width:100%'>");
page += tpage;
page += F("</table>");
}
if (Maxdevice) {
page += F("<table style='width:100%'><tr>");
for (byte idx = 1; idx <= Maxdevice; idx++) {
page += F("<td style='width:{1%'><div style='text-align:center;font-weight:bold;font-size:{2px'>{3</div></td>");
page.replace("{1", String(100 / Maxdevice));
page.replace("{2", String(70 - (Maxdevice * 8)));
page.replace("{3", (power & (0x01 << (idx -1))) ? "ON" : "OFF");
}
page += F("</tr></table>");
}
webServer->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
webServer->sendHeader("Pragma", "no-cache");
webServer->sendHeader("Expires", "-1");
webServer->send(200, "text/plain", page);
}
boolean httpUser()
@ -1119,15 +1153,9 @@ void handleCmnd()
void handleConsole()
{
if (httpUser()) return;
char svalue[MESSZ];
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle console"));
if (strlen(webServer->arg("c1").c_str())) {
snprintf_P(svalue, sizeof(svalue), webServer->arg("c1").c_str());
do_cmnd(svalue);
}
String page = FPSTR(HTTP_HEAD);
page.replace("{v}", "Console");
page.replace("<body>", "<body onload='l()'>");
@ -1139,29 +1167,44 @@ void handleConsole()
void handleAjax()
{
if (httpUser()) return;
String message = "";
uint16_t size = 0;
char svalue[MESSZ], log[LOGSZ];
byte counter = 99;
int maxSize = ESP.getFreeHeap() - 6000;
if (strlen(webServer->arg("c1").c_str())) {
snprintf_P(svalue, sizeof(svalue), webServer->arg("c1").c_str());
snprintf_P(log, sizeof(log), PSTR("CMND: %s"), svalue);
addLog(LOG_LEVEL_INFO, log);
do_cmnd(svalue);
}
if (strlen(webServer->arg("c2").c_str())) counter = atoi(webServer->arg("c2").c_str());
byte counter = logidx;
do {
counter--;
if (counter == 255) counter = MAX_LOG_LINES -1;
size += Log[counter].length();
} while ((counter != logidx) && (size < maxSize));
do {
if (Log[counter].length()) {
if (message.length()) message += F("\n");
message += Log[counter];
}
counter++;
if (counter > MAX_LOG_LINES -1) counter = 0;
} while (counter != logidx);
String message = F("<r><i>");
message += String(logidx);
message += F("</i><j>");
message += String(logajaxflg);
if (!logajaxflg) {
counter = 99;
logajaxflg = 1;
}
message += F("</j><l>");
if (counter != logidx) {
if (counter == 99) counter = logidx;
do {
if (Log[counter].length()) {
message += F("\n");
message += Log[counter];
}
counter++;
if (counter > MAX_LOG_LINES -1) counter = 0;
} while (counter != logidx);
}
message += F("</l></r>");
webServer->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
webServer->sendHeader("Pragma", "no-cache");
webServer->sendHeader("Expires", "-1");
webServer->send(200, "text/plain", message);
webServer->send(200, "text/xml", message);
}
void handleInfo()
@ -1274,9 +1317,6 @@ void handleRestart()
restartflag = 2;
}
/********************************************************************************************/
/********************************************************************************************/
void handleNotFound()