mirror of https://github.com/arendst/Tasmota.git
v3.9.17
3.9.17 20170217 * Fix possible ArduinoJSON related memory fragmentation * Changed console logging using less memory * Add GPIO04 as user selectable for Sonoff Dual (#75)
This commit is contained in:
parent
381bb4b50c
commit
edca508f57
|
@ -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.16** - See ```sonoff/_releasenotes.ino``` for change information.
|
||||
Current version is **3.9.17** - 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.
|
@ -1,4 +1,9 @@
|
|||
/* 3.9.16 20170214
|
||||
/* 3.9.17 20170217
|
||||
* Fix possible ArduinoJSON related memory fragmentation
|
||||
* Changed console logging using less memory
|
||||
* Add GPIO04 as user selectable for Sonoff Dual (#75)
|
||||
*
|
||||
* 3.9.16 20170214
|
||||
* Update latching relay handler
|
||||
* Add support for IR led using IRremoteESP8266 library (#59)
|
||||
* Add Hue argument passing using ArduinoJSON library (#59)
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
* ====================================================
|
||||
*/
|
||||
|
||||
#define VERSION 0x03091000 // 3.9.16
|
||||
#define VERSION 0x03091100 // 3.9.17
|
||||
|
||||
//#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
|
||||
|
@ -127,7 +127,7 @@ enum emul_t {EMUL_NONE, EMUL_WEMO, EMUL_HUE, EMUL_MAX};
|
|||
#ifdef USE_MQTT_TLS
|
||||
#define MAX_LOG_LINES 10 // Max number of lines in weblog
|
||||
#else
|
||||
#define MAX_LOG_LINES 60 // Max number of lines in weblog
|
||||
#define MAX_LOG_LINES 20 // Max number of lines in weblog
|
||||
#endif
|
||||
|
||||
#define APP_BAUDRATE 115200 // Default serial baudrate
|
||||
|
@ -141,6 +141,7 @@ enum butt_t {PRESSED, NOT_PRESSED};
|
|||
#include <ESP8266HTTPClient.h> // MQTT, Ota
|
||||
#include <ESP8266httpUpdate.h> // Ota
|
||||
#include <PubSubClient.h> // MQTT
|
||||
#include <ArduinoJson.h> // WemoHue, IRremote, Domoticz
|
||||
#ifdef USE_WEBSERVER
|
||||
#include <ESP8266WebServer.h> // WifiManager, Webserver
|
||||
#include <DNSServer.h> // WifiManager
|
||||
|
@ -154,12 +155,6 @@ enum butt_t {PRESSED, NOT_PRESSED};
|
|||
#ifdef USE_I2C
|
||||
#include <Wire.h> // I2C support library
|
||||
#endif // USE_I2C
|
||||
#if defined USE_EMULATION || defined USE_IR_REMOTE
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
const size_t bufferSize = JSON_ARRAY_SIZE(2) + JSON_OBJECT_SIZE(10) + 130; // Required size for complete HUE light JSON object or other JSON objects
|
||||
DynamicJsonBuffer jsonBuffer(bufferSize);
|
||||
#endif // USE_EMULATION || USE_IR_REMOTE
|
||||
|
||||
typedef void (*rtcCallback)();
|
||||
|
||||
|
@ -900,6 +895,7 @@ void sl_setColor(byte type)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void json2legacy(char* stopic, char* svalue)
|
||||
{
|
||||
char *p, *token;
|
||||
|
@ -2458,7 +2454,7 @@ void GPIO_init()
|
|||
uint8_t mpin;
|
||||
mytmplt def_module;
|
||||
|
||||
if (!sysCfg.module || (sysCfg.module >= MAXMODULE)) sysCfg.module = SONOFF_BASIC; // Sonoff Basic
|
||||
if (!sysCfg.module || (sysCfg.module >= MAXMODULE)) sysCfg.module = MODULE;
|
||||
|
||||
memcpy_P(&def_module, &modules[sysCfg.module], sizeof(def_module));
|
||||
strlcpy(my_module.name, def_module.name, sizeof(my_module.name));
|
||||
|
|
|
@ -155,7 +155,8 @@ const mytmplt modules[MAXMODULE] PROGMEM = {
|
|||
GPIO_TXD, // GPIO01 Relay control
|
||||
0,
|
||||
GPIO_RXD, // GPIO03 Relay control
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
GPIO_USER, // GPIO04 Optional sensor
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off)
|
||||
0, 0, 0
|
||||
},
|
||||
|
|
|
@ -60,8 +60,19 @@ const char HTTP_HEAD[] PROGMEM =
|
|||
"var x=new XMLHttpRequest();"
|
||||
"x.onreadystatechange=function(){"
|
||||
"if(x.readyState==4&&x.status==200){"
|
||||
"e.value=x.responseText;"
|
||||
"e.scrollTop=100000;"
|
||||
"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;"
|
||||
"}"
|
||||
"}"
|
||||
"e.scrollTop=99999;"
|
||||
"sn=e.scrollTop;"
|
||||
"}"
|
||||
"};"
|
||||
|
@ -75,7 +86,7 @@ const char HTTP_HEAD[] PROGMEM =
|
|||
"div,fieldset,input,select{padding:5px;font-size:1em;}"
|
||||
"input{width:95%;}"
|
||||
"select{width:100%;}"
|
||||
"textarea{resize:none;width:98%;height:312px;padding:5px;overflow:auto;}"
|
||||
"textarea{resize:none;width:98%;height:318px;padding:5px;overflow:auto;}"
|
||||
"body{text-align:center;font-family:verdana;}"
|
||||
"td{padding:0px;}"
|
||||
"button{border:0;border-radius:0.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%;-webkit-transition-duration:0.4s;transition-duration:0.4s;}"
|
||||
|
@ -197,9 +208,9 @@ const char HTTP_FORM_UPG[] PROGMEM =
|
|||
"</div>"
|
||||
"<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='80' wrap='off'></textarea><br/><br/>"
|
||||
"<br/><textarea readonly id='t1' name='t1' cols='99' wrap='off'></textarea><br/><br/>"
|
||||
"<form method='post' action='cs'>"
|
||||
"<input style='width:98%' id='c1' name='c1' length=80 placeholder='Enter command' autofocus><br/>"
|
||||
"<input style='width:98%' id='c1' name='c1' length='99' placeholder='Enter command' autofocus><br/>"
|
||||
// "<br/><button type='submit'>Send command</button>"
|
||||
"</form>";
|
||||
const char HTTP_COUNTER[] PROGMEM =
|
||||
|
@ -427,7 +438,7 @@ void pollDnsWeb()
|
|||
void showPage(String &page)
|
||||
{
|
||||
page.replace("{ha}", my_module.name);
|
||||
page.replace("{h}", String(sysCfg.friendlyname[0]));
|
||||
page.replace("{h}", sysCfg.friendlyname[0]);
|
||||
if (_httpflag == HTTP_MANAGER) {
|
||||
if (WIFI_configCounter()) {
|
||||
page.replace("<body>", "<body onload='u()'>");
|
||||
|
@ -681,11 +692,11 @@ void handleWifi(boolean scan)
|
|||
}
|
||||
|
||||
page += FPSTR(HTTP_FORM_WIFI);
|
||||
page.replace("{h1}", String(sysCfg.hostname));
|
||||
page.replace("{s1}", String(sysCfg.sta_ssid[0]));
|
||||
page.replace("{p1}", String(sysCfg.sta_pwd[0]));
|
||||
page.replace("{s2}", String(sysCfg.sta_ssid[1]));
|
||||
page.replace("{p2}", String(sysCfg.sta_pwd[1]));
|
||||
page.replace("{h1}", sysCfg.hostname);
|
||||
page.replace("{s1}", sysCfg.sta_ssid[0]);
|
||||
page.replace("{p1}", sysCfg.sta_pwd[0]);
|
||||
page.replace("{s2}", sysCfg.sta_ssid[1]);
|
||||
page.replace("{p2}", sysCfg.sta_pwd[1]);
|
||||
page += FPSTR(HTTP_FORM_END);
|
||||
if (_httpflag == HTTP_MANAGER) {
|
||||
page += FPSTR(HTTP_BTN_RSTRT);
|
||||
|
@ -706,12 +717,12 @@ void handleMqtt()
|
|||
char str[sizeof(sysCfg.mqtt_client)];
|
||||
getClient(str, MQTT_CLIENT_ID, sizeof(sysCfg.mqtt_client));
|
||||
page.replace("{m0}", str);
|
||||
page.replace("{m1}", String(sysCfg.mqtt_host));
|
||||
page.replace("{m1}", sysCfg.mqtt_host);
|
||||
page.replace("{m2}", String(sysCfg.mqtt_port));
|
||||
page.replace("{m3}", String(sysCfg.mqtt_client));
|
||||
page.replace("{m4}", String(sysCfg.mqtt_user));
|
||||
page.replace("{m5}", String(sysCfg.mqtt_pwd));
|
||||
page.replace("{m6}", String(sysCfg.mqtt_topic));
|
||||
page.replace("{m3}", sysCfg.mqtt_client);
|
||||
page.replace("{m4}", sysCfg.mqtt_user);
|
||||
page.replace("{m5}", sysCfg.mqtt_pwd);
|
||||
page.replace("{m6}", sysCfg.mqtt_topic);
|
||||
page += FPSTR(HTTP_FORM_END);
|
||||
page += FPSTR(HTTP_BTN_CONF);
|
||||
showPage(page);
|
||||
|
@ -755,7 +766,7 @@ void handleLog()
|
|||
}
|
||||
}
|
||||
page += FPSTR(HTTP_FORM_LOG3);
|
||||
page.replace("{l2}", String(sysCfg.syslog_host));
|
||||
page.replace("{l2}", sysCfg.syslog_host);
|
||||
page.replace("{l3}", String(sysCfg.syslog_port));
|
||||
page.replace("{l4}", String(sysCfg.tele_period));
|
||||
page += FPSTR(HTTP_FORM_END);
|
||||
|
@ -776,7 +787,7 @@ void handleOther()
|
|||
page += FPSTR(HTTP_FORM_OTHER2);
|
||||
page.replace("{1", "1");
|
||||
page.replace("{2", FRIENDLY_NAME);
|
||||
page.replace("{3", String(sysCfg.friendlyname[0]));
|
||||
page.replace("{3", sysCfg.friendlyname[0]);
|
||||
#ifdef USE_EMULATION
|
||||
page += FPSTR(HTTP_FORM_OTHER3);
|
||||
page.replace("{r2}", (sysCfg.emulation == EMUL_NONE) ? " checked" : "");
|
||||
|
@ -787,7 +798,7 @@ void handleOther()
|
|||
page.replace("{1", String(i +1));
|
||||
snprintf_P(stemp, sizeof(stemp), PSTR(FRIENDLY_NAME"%d"), i +1);
|
||||
page.replace("{2", stemp);
|
||||
page.replace("{3", String(sysCfg.friendlyname[i]));
|
||||
page.replace("{3", sysCfg.friendlyname[i]);
|
||||
}
|
||||
page += F("<br/></fieldset>");
|
||||
#endif // USE_EMULATION
|
||||
|
@ -966,7 +977,7 @@ void handleUpgrade()
|
|||
String page = FPSTR(HTTP_HEAD);
|
||||
page.replace("{v}", "Firmware upgrade");
|
||||
page += FPSTR(HTTP_FORM_UPG);
|
||||
page.replace("{o1}", String(sysCfg.otaUrl));
|
||||
page.replace("{o1}", sysCfg.otaUrl);
|
||||
page += FPSTR(HTTP_BTN_MAIN);
|
||||
showPage(page);
|
||||
|
||||
|
@ -1269,7 +1280,7 @@ void handleInfo()
|
|||
for (byte i = 0; i < Maxdevice; i++) {
|
||||
page += F("<tr><th>Friendly name ");
|
||||
page += i +1;
|
||||
page += F("</th><td>"); page += String(sysCfg.friendlyname[i]); page += F("</td></tr>");
|
||||
page += F("</th><td>"); page += sysCfg.friendlyname[i]; page += F("</td></tr>");
|
||||
}
|
||||
page += F("<tr><td> </td></tr>");
|
||||
// page += F("<tr><th>SSId (RSSI)</th><td>"); page += (sysCfg.sta_active)? sysCfg.sta_ssid2 : sysCfg.sta_ssid1; page += F(" ("); page += WIFI_getRSSIasQuality(WiFi.RSSI()); page += F("%)</td></tr>");
|
||||
|
@ -1387,7 +1398,7 @@ void handleUPnPsetupWemo()
|
|||
addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Handle WeMo setup"));
|
||||
|
||||
String setup_xml = FPSTR(WEMO_SETUP_XML);
|
||||
setup_xml.replace("{x1}", String(sysCfg.friendlyname[0]));
|
||||
setup_xml.replace("{x1}", sysCfg.friendlyname[0]);
|
||||
setup_xml.replace("{x2}", wemo_UUID());
|
||||
setup_xml.replace("{x3}", wemo_serial());
|
||||
webServer->send(200, "text/xml", setup_xml);
|
||||
|
@ -1432,7 +1443,7 @@ void hue_config_response(String *response)
|
|||
response->replace("{gw}", WiFi.gatewayIP().toString());
|
||||
snprintf_P(buffer, sizeof(buffer), PSTR("%04d-%02d-%02dT%02d:%02d:%02d"),
|
||||
rtcTime.Year, rtcTime.Month, rtcTime.Day, rtcTime.Hour, rtcTime.Minute, rtcTime.Second);
|
||||
response->replace("{dt}", String(buffer));
|
||||
response->replace("{dt}", buffer);
|
||||
}
|
||||
|
||||
void hue_global_cfg(String *path)
|
||||
|
@ -1471,13 +1482,9 @@ void hue_global_cfg(String *path)
|
|||
|
||||
void hue_auth(String *path)
|
||||
{
|
||||
String response;
|
||||
char uid[7];
|
||||
char response[38];
|
||||
|
||||
snprintf_P(uid, sizeof(uid), PSTR("%03x"), ESP.getChipId());
|
||||
response="[{\"success\":{\"username\":\"";
|
||||
response+=String(uid);
|
||||
response+="\"}}]";
|
||||
snprintf_P(response, sizeof(response), PSTR("[{\"success\":{\"username\":\"%03x\"}}]"), ESP.getChipId());
|
||||
webServer->send(200, "application/json", response);
|
||||
}
|
||||
|
||||
|
@ -1493,6 +1500,9 @@ void hue_config(String *path)
|
|||
|
||||
void hue_lights(String *path)
|
||||
{
|
||||
/*
|
||||
* http://sonoff/api/username/lights/1/state?1={"on":true,"hue":56100,"sat":254,"bri":254,"alert":"none","transitiontime":40}
|
||||
*/
|
||||
String response;
|
||||
uint8_t device = 1;
|
||||
int16_t pos = 0;
|
||||
|
@ -1530,7 +1540,7 @@ void hue_lights(String *path)
|
|||
else if (path->endsWith("/state")) // Got ID/state
|
||||
{
|
||||
path->remove(0,8); // Remove /lights/
|
||||
path->remove(path->indexOf("/state")); // Remove /state
|
||||
path->remove(path->indexOf("/state")); // Remove /state
|
||||
device = atoi(path->c_str());
|
||||
if ((device < 1) || (device > Maxdevice)) device = 1;
|
||||
response = "[";
|
||||
|
@ -1540,6 +1550,7 @@ void hue_lights(String *path)
|
|||
response.replace("{cmd}", "state/on");
|
||||
if (webServer->args() == 1)
|
||||
{
|
||||
StaticJsonBuffer<400> jsonBuffer;
|
||||
JsonObject &hue_json = jsonBuffer.parseObject(webServer->arg(0));
|
||||
on = hue_json["on"];
|
||||
switch(on)
|
||||
|
|
|
@ -40,36 +40,6 @@ const char domoticz_sensors[DOMOTICZ_MAX_SENSORS][14] PROGMEM =
|
|||
int domoticz_update_timer = 0;
|
||||
byte domoticz_update_flag = 1;
|
||||
|
||||
unsigned long getKeyIntValue(const char *json, const char *key)
|
||||
{
|
||||
char *p, *b;
|
||||
int i;
|
||||
|
||||
// search key
|
||||
p = strstr(json, key);
|
||||
if (!p) return 0;
|
||||
// search following separator :
|
||||
b = strchr(p + strlen(key), ':');
|
||||
if (!b) return 0;
|
||||
// Only the following chars are allowed between key and separator :
|
||||
for(i = b - json + strlen(key); i < p-json; i++) {
|
||||
switch (json[i]) {
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\t':
|
||||
case '\r':
|
||||
continue;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
b++;
|
||||
// Allow integers as string too (used in "svalue" : "9")
|
||||
while ((b[0] == ' ') || (b[0] == '"')) b++;
|
||||
// Convert to integer
|
||||
return atoi(b);
|
||||
}
|
||||
|
||||
void mqtt_publishDomoticzPowerState(byte device)
|
||||
{
|
||||
char svalue[MESSZ];
|
||||
|
@ -130,6 +100,23 @@ boolean domoticz_update()
|
|||
return domoticz_update_flag;
|
||||
}
|
||||
|
||||
/*
|
||||
* ArduinoJSON Domoticz Switch entry used to calculate jsonBuf: JSON_OBJECT_SIZE(11) + 129 = 313
|
||||
{
|
||||
"Battery" : 255,
|
||||
"RSSI" : 12,
|
||||
"dtype" : "Light/Switch",
|
||||
"id" : "000140E7",
|
||||
"idx" : 159,
|
||||
"name" : "Sonoff1",
|
||||
"nvalue" : 1,
|
||||
"stype" : "Switch",
|
||||
"svalue1" : "0",
|
||||
"switchType" : "Dimmer",
|
||||
"unit" : 1
|
||||
}
|
||||
*/
|
||||
|
||||
boolean domoticz_mqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uint16_t sdataBuf)
|
||||
{
|
||||
char log[LOGSZ], stemp1[10];
|
||||
|
@ -139,8 +126,12 @@ boolean domoticz_mqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uin
|
|||
domoticz_update_flag = 1;
|
||||
if (!strncmp(topicBuf, sysCfg.domoticz_out_topic, strlen(sysCfg.domoticz_out_topic)) != 0) {
|
||||
if (sdataBuf < 20) return 1;
|
||||
idx = getKeyIntValue(dataBuf,"\"idx\"");
|
||||
nvalue = getKeyIntValue(dataBuf,"\"nvalue\"");
|
||||
StaticJsonBuffer<400> jsonBuf;
|
||||
JsonObject& domoticz = jsonBuf.parseObject(dataBuf);
|
||||
if (!domoticz.success()) return 1;
|
||||
// if (strcmp(domoticz["dtype"],"Light/Switch")) return 1;
|
||||
idx = domoticz["idx"];
|
||||
nvalue = domoticz["nvalue"];
|
||||
|
||||
snprintf_P(log, sizeof(log), PSTR("DMTZ: idx %d, nvalue %d"), idx, nvalue);
|
||||
addLog(LOG_LEVEL_DEBUG_MORE, log);
|
||||
|
@ -150,7 +141,7 @@ boolean domoticz_mqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uin
|
|||
if ((idx > 0) && (idx == sysCfg.domoticz_relay_idx[i])) {
|
||||
snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), i +1);
|
||||
if (nvalue == 2) {
|
||||
nvalue = getKeyIntValue(dataBuf,"\"svalue1\"");
|
||||
nvalue = domoticz["svalue1"];
|
||||
if ((pin[GPIO_WS2812] < 99) && (sysCfg.ws_dimmer == nvalue)) return 1;
|
||||
if ((sysCfg.module == SONOFF_LED) && (sysCfg.led_dimmer[i] == nvalue)) return 1;
|
||||
snprintf_P(topicBuf, stopicBuf, PSTR("%s/%s/DIMMER%s"),
|
||||
|
|
|
@ -42,6 +42,11 @@ void ir_send_init(void)
|
|||
* Commands
|
||||
\*********************************************************************************************/
|
||||
|
||||
/*
|
||||
* ArduinoJSON IRsend entry used to calculate jsonBuf: JSON_OBJECT_SIZE(3) + 40 = 96
|
||||
{ "protocol": "SAMSUNG", "bits": 32, "data": 551502015 }
|
||||
*/
|
||||
|
||||
boolean ir_send_command(char *type, uint16_t index, char *dataBuf, uint16_t data_len, int16_t payload, char *svalue, uint16_t ssvalue)
|
||||
{
|
||||
boolean serviced = true;
|
||||
|
@ -52,7 +57,8 @@ boolean ir_send_command(char *type, uint16_t index, char *dataBuf, uint16_t data
|
|||
|
||||
if (!strcmp(type,"IRSEND")) {
|
||||
if (data_len) {
|
||||
JsonObject &ir_json = jsonBuffer.parseObject(dataBuf);
|
||||
StaticJsonBuffer<128> jsonBuf;
|
||||
JsonObject &ir_json = jsonBuf.parseObject(dataBuf);
|
||||
if (!ir_json.success()) {
|
||||
snprintf_P(svalue, ssvalue, PSTR("{\"IRSend\":\"Invalid JSON\"}")); // JSON decode failed
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue