Merge branch 'development' into backlog-if

This commit is contained in:
Laurent Dong 2019-12-11 11:32:59 -05:00
commit 7f28b1d12a
46 changed files with 457 additions and 71 deletions

View File

@ -47,10 +47,23 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
## Changelog
### Version 7.1.2 Betty
### Version 7.1.2.4
- Fix lost functionality of GPIO9 and GPIO10 on some devices (#7080)
- Fix Zigbee uses Hardware Serial if GPIO 1/3 or GPIO 13/15 and SerialLog 0 (#7071)
- Fix WS2812 power control (#7090)
- Change light color schemes 2, 3 and 4 from color wheel to Hue driven with user Saturation control
- Change log buffer size from 520 to 700 characters accomodating full rule text (#7110)
- Change Exception reporting removing exception details from ``Status 1`` and consolidated in ``Status 12`` if available
- Change HTTP CORS from command ``SetOption73 0/1`` to ``Cors <cors_domain>`` allowing user control of specific CORS domain by Shantur Rathore (#7066)
- Change GUI Shutter button text to Up and Down Arrows based on PR by Xavier Muller (#7166)
- Change amount of supported DHT sensors from 3 to 4 by Xavier Muller (#7167)
- Fix flashing H801 led at boot by Stefan Hadinger (#7165, #649)
- Fix duplicated ``Backlog`` when using Event inside a Backlog by Adrian Scillato (#7178, #7147)
- Fix Gui Timer when using a negative zero offset of -00:00 by Peter Ooms (#7174)
- Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config based in PR by Luis Teixeira (#7108)
- Add rule var ``%topic%`` by Adrian Scillato (#5522)
- Add rule triggers ``tele-wifi1#xxx`` by Adrian Scillato (#7093)
- Add SML bus decoder syntax support for byte order by Gerhard Mutz (#7112)
- Add experimental support for stepper motor shutter control by Stefan Bode
- Add optional USE_MQTT_TLS to tasmota-minimal.bin by Bohdan Kmit (#7115)
- Add save call stack in RTC memory in case of crash, command ``Status 12`` to dump the stack by Stefan Hadinger
- Add Home Assistant force update by Frederico Leoni (#7140, #7074)
- Add Wifi Signal Strength in dBm in addition to RSSI Wifi Experience by Andreas Schultz (#7145)
- Add Yaw, Pitch and Roll support for MPU6050 by Philip Barclay (#7058)
- Add reporting of raw weight to JSON from HX711 to overcome auto-tare functionality by @tobox (#7171)

View File

@ -32,8 +32,18 @@
#define DISABLE_RESTORE_BUTTON 1 // [Default 0] Set to 1 to disable the "restore defaults" button in the web ui.
// These values normally don't need adjustment
#define MULTICAST_PORT 3671 // [Default 3671]
#ifndef MULTICAST_IP
#define MULTICAST_IP IPAddress(224, 0, 23, 12) // [Default IPAddress(224, 0, 23, 12)]
#else
#warning USING CUSTOM MULTICAST_IP
#endif
#ifndef MULTICAST_PORT
#define MULTICAST_PORT 3671 // [Default 3671]
#else
#warning USING CUSTOM MULTICAST_PORT
#endif
#define SEND_CHECKSUM 0
// Uncomment to enable printing out debug messages.

View File

@ -1,15 +1,36 @@
## Unreleased (development)
### 7.1.2.4 20191209
- Change HTTP CORS from command ``SetOption73 0/1`` to ``Cors <cors_domain>`` allowing user control of specific CORS domain by Shantur Rathore (#7066)
- Change GUI Shutter button text to Up and Down Arrows based on PR by Xavier Muller (#7166)
- Change amount of supported DHT sensors from 3 to 4 by Xavier Muller (#7167)
- Revert removal of exception details from MQTT info on restart
- Add Wifi Signal Strength in dBm in addition to RSSI Wifi Experience by Andreas Schultz (#7145)
- Add Yaw, Pitch and Roll support for MPU6050 by Philip Barclay (#7058)
- Add reporting of raw weight to JSON from HX711 to overcome auto-tare functionality by @tobox (#7171)
- Fix flashing H801 led at boot by Stefan Hadinger (#7165, #649)
- Fix duplicated ``Backlog`` when using Event inside a Backlog by Adrian Scillato (#7178, #7147)
- Fix Gui Timer when using a negative zero offset of -00:00 by Peter Ooms (#7174)
### 7.1.2.3 20191208
- Change Exception reporting removing exception details from both MQTT info and ``Status 1``. Now consolidated in ``Status 12`` if available.
### 7.1.2.2 20191206
- Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config (#7108)
- Remove rule trigger ``tele_power1#state`` due to compatibility
- Add command ``SerialConfig 0..23`` or ``SerialConfig 8N1`` to select Serial Config based in PR by Luis Teixeira (#7108)
- Add save call stack in RTC memory in case of crash, command ``Status 12`` to dump the stack by Stefan Hadinger
- Add Home Assistant force update by Frederico Leoni (#7140, #7074)
### 7.1.2.1 20191206
- Add rule var ``%topic%`` (#5522)
- Add rule triggers ``tele_power1#state`` and multiple ``tele-wifi1#xxx`` (#7093)
- Add experimental support for stepper motor shutter control
- Add optional USE_MQTT_TLS to tasmota-minimal.bin (#7115)
- Add SML bus decoder syntax support for byte order by Gerhard Mutz (#7112)
- Add rule var ``%topic%`` by Adrian Scillato (#5522)
- Add rule triggers ``tele_power1#state`` and multiple ``tele-wifi1#xxx`` by Adrian Scillato (#7093)
- Add experimental support for stepper motor shutter control by Stefan Bode
- Add optional USE_MQTT_TLS to tasmota-minimal.bin by Bohdan Kmit (#7115)
## Released

View File

@ -134,6 +134,7 @@
#define D_JSON_SELECTED "selected"
#define D_JSON_SERIALRECEIVED "SerialReceived"
#define D_JSON_SET "Set"
#define D_JSON_SIGNAL "Signal"
#define D_JSON_SSID "SSId"
#define D_JSON_STARTDST "StartDST" // Start Daylight Savings Time
#define D_JSON_STARTED "Started"
@ -208,6 +209,7 @@
#define D_STATUS9_MARGIN "PTH"
#define D_STATUS10_SENSOR "SNS"
#define D_STATUS11_STATUS "STS"
#define D_STATUS12_STATUS "STK"
#define D_CMND_STATE "State"
#define D_CMND_POWER "Power"
#define D_CMND_FANSPEED "FanSpeed"
@ -333,6 +335,7 @@
#define D_CMND_WEBSENSOR "WebSensor"
#define D_CMND_EMULATION "Emulation"
#define D_CMND_SENDMAIL "Sendmail"
#define D_CMND_CORS "CORS"
// Commands xdrv_03_energy.ino
#define D_CMND_POWERLOW "PowerLow"

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "Хладна"
#define D_COMMAND "Команда"
#define D_CONNECTED "Свързан"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "Брой"
#define D_COUNTER "Брояч"
#define D_CURRENT "Ток" // As in Voltage and Current

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "Studené světlo"
#define D_COMMAND "Příkaz"
#define D_CONNECTED "...připojeno"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "Počítej"
#define D_COUNTER "Počítadlo"
#define D_CURRENT "Proud" // As in Voltage and Current

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "kalt"
#define D_COMMAND "Befehl"
#define D_CONNECTED "verbunden"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "zählen"
#define D_COUNTER "Zähler"
#define D_CURRENT "Strom" // As in Voltage and Current

View File

@ -72,6 +72,7 @@
#define D_COMMAND "Εντολή"
#define D_CONNECTED "Συνδεδεμένο"
#define D_COUNT "Μέτρηση"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNTER "Μετρητής"
#define D_CURRENT "Ένταση" // As in Voltage and Current
#define D_DATA "Δεδομένα"

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "Cold"
#define D_COMMAND "Command"
#define D_CONNECTED "Connected"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "Count"
#define D_COUNTER "Counter"
#define D_CURRENT "Current" // As in Voltage and Current

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "Fría"
#define D_COMMAND "Comando"
#define D_CONNECTED "Conectado"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "Conteo"
#define D_COUNTER "Contador"
#define D_CURRENT "Corriente" // As in Voltage and Current

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "Froid"
#define D_COMMAND "Commande"
#define D_CONNECTED "Connecté"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "Compte"
#define D_COUNTER "Compteur"
#define D_CURRENT "Courant" // As in Voltage and Current

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "אור קר"
#define D_COMMAND "פקודה"
#define D_CONNECTED "מחובר"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "סופר"
#define D_COUNTER "מונה"
#define D_CURRENT "נוכחי" // As in Voltage and Current

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "Hideg fény"
#define D_COMMAND "Parancs"
#define D_CONNECTED "Csatlakoztatva"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "Szám"
#define D_COUNTER "Számláló"
#define D_CURRENT "Áramerősség" // As in Voltage and Current

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "Fredda"
#define D_COMMAND "Comando"
#define D_CONNECTED "Connesso"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "Conteggio"
#define D_COUNTER "Contatore"
#define D_CURRENT "Corrente" // As in Voltage and Current

View File

@ -72,6 +72,7 @@
#define D_COMMAND "커맨드"
#define D_CONNECTED "연결됨"
#define D_COUNT "횟수"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNTER "Counter"
#define D_CURRENT "전류" // As in Voltage and Current
#define D_DATA "Data"

View File

@ -72,6 +72,7 @@
#define D_COMMAND "Opdracht"
#define D_CONNECTED "Verbonden"
#define D_COUNT "Aantal"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNTER "Teller"
#define D_CURRENT "Stroom" // As in Voltage and Current
#define D_DATA "Data"

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "Zimny"
#define D_COMMAND "Komenda"
#define D_CONNECTED "Połączony"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "Licz"
#define D_COUNTER "Licznik"
#define D_CURRENT "Prąd" // As in Voltage and Current

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "Luz fria"
#define D_COMMAND "Comando"
#define D_CONNECTED "Ligado"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "Contagem"
#define D_COUNTER "Contador"
#define D_CURRENT "Corrente" // As in Voltage and Current

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "Luz Fria"
#define D_COMMAND "Comando"
#define D_CONNECTED "Ligado"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "Contagem"
#define D_COUNTER "Contador"
#define D_CURRENT "Corrente" // As in Voltage and Current

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "Холодный"
#define D_COMMAND "Команда"
#define D_CONNECTED "Соединен"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "Подсчет"
#define D_COUNTER "Счетчик"
#define D_CURRENT "Ток" // As in Voltage and Current

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "Studené svetlo"
#define D_COMMAND "Príkaz"
#define D_CONNECTED "...pripojené"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "Počítaj"
#define D_COUNTER "Počítadlo"
#define D_CURRENT "Prúd" // As in Voltage and Current

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "Kallt"
#define D_COMMAND "Kommando"
#define D_CONNECTED "Ansluten"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "Räkna"
#define D_COUNTER "Räknare"
#define D_CURRENT "Ström" // As in Voltage and Current

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "Soğuk"
#define D_COMMAND "Komut"
#define D_CONNECTED "Bağlandı"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "Sayı"
#define D_COUNTER "Sayaç"
#define D_CURRENT "Current" // As in Voltage and Current

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "Холодний"
#define D_COMMAND "Команда"
#define D_CONNECTED "Під'єднано"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "Розмір"
#define D_COUNTER "Лічильник"
#define D_CURRENT "Струм" // As in Voltage and Current

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "冷"
#define D_COMMAND "命令:"
#define D_CONNECTED "已连接"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "数量:"
#define D_COUNTER "计数器"
#define D_CURRENT "电流" // As in Voltage and Current

View File

@ -71,6 +71,7 @@
#define D_COLDLIGHT "冷"
#define D_COMMAND "命令:"
#define D_CONNECTED "已連接"
#define D_CORS_DOMAIN "CORS Domain"
#define D_COUNT "數量:"
#define D_COUNTER "Counter"
#define D_CURRENT "電流" // As in Voltage and Current

View File

@ -132,6 +132,7 @@
#define WEB_PASSWORD "" // [WebPassword] Web server Admin mode Password for WEB_USERNAME (empty string = Disable)
#define FRIENDLY_NAME "Tasmota" // [FriendlyName] Friendlyname up to 32 characters used by webpages and Alexa
#define EMULATION EMUL_NONE // [Emulation] Select Belkin WeMo (single relay/light) or Hue Bridge emulation (multi relay/light) (EMUL_NONE, EMUL_WEMO or EMUL_HUE)
#define CORS_DOMAIN "" // [Cors] CORS Domain for preflight requests
// -- HTTP GUI Colors -----------------------------
// HTML hex color codes. Only 3 and 6 digit hex string values are supported!! See https://www.w3schools.com/colors/colors_hex.asp
@ -284,7 +285,7 @@
#define DOMOTICZ_OUT_TOPIC "domoticz/out" // Domoticz Output Topic
// -- MQTT - Home Assistant Discovery -------------
#define USE_HOME_ASSISTANT // Enable Home Assistant Discovery Support (+7k code)
#define USE_HOME_ASSISTANT // Enable Home Assistant Discovery Support (+4.1k code, +6 bytes mem)
#define HOME_ASSISTANT_DISCOVERY_PREFIX "homeassistant" // Home Assistant discovery prefix
// -- MQTT - TLS - AWS IoT ------------------------

View File

@ -86,7 +86,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
uint32_t energy_weekend : 1; // bit 20 (v6.6.0.8) - CMND_TARIFF
uint32_t dds2382_model : 1; // bit 21 (v6.6.0.14) - SetOption71 - Select different Modbus registers for Active Energy (#6531)
uint32_t hardware_energy_total : 1; // bit 22 (v6.6.0.15) - SetOption72 - Enable hardware energy total counter as reference (#6561)
uint32_t cors_enabled : 1; // bit 23 (v7.0.0.1) - SetOption73 - Enable HTTP CORS
uint32_t ex_cors_enabled : 1; // bit 23 (v7.0.0.1) - SetOption73 - Enable HTTP CORS
uint32_t ds18x20_internal_pullup : 1; // bit 24 (v7.0.0.1) - SetOption74 - Enable internal pullup for single DS18x20 sensor
uint32_t grouptopic_mode : 1; // bit 25 (v7.0.0.1) - SetOption75 - GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1)
uint32_t bootcount_update : 1; // bit 26 (v7.0.0.4) - SetOption76 - Enable incrementing bootcount when deepsleep is enabled
@ -433,12 +433,11 @@ struct SYSCFG {
uint16_t energy_power_delta; // E98
uint8_t shutter_motordelay[MAX_SHUTTERS]; // E9A
int8_t temp_comp; // E9E
uint8_t free_e9f[1]; // E9F
uint8_t weight_change; // E9F
uint8_t web_color2[2][3]; // EA0 - Needs to be on integer / 3 distance from web_color
char cors_domain[33]; // EA6
uint8_t free_ea4[326]; // EA6
uint8_t free_ec1[293]; // EC1
uint32_t i2c_drivers[3]; // FEC I2cDriver
uint32_t cfg_timestamp; // FF8

View File

@ -140,6 +140,10 @@
#ifndef DEFAULT_LIGHT_COMPONENT
#define DEFAULT_LIGHT_COMPONENT 255
#endif
#ifndef CORS_ENABLED_ALL
#define CORS_ENABLED_ALL "*"
#endif
enum WebColors {
COL_TEXT, COL_BACKGROUND, COL_FORM,
@ -718,6 +722,7 @@ void SettingsDefaultSet2(void)
Settings.weblog_level = WEB_LOG_LEVEL;
strlcpy(Settings.web_password, WEB_PASSWORD, sizeof(Settings.web_password));
Settings.flag3.mdns_enabled = MDNS_ENABLED;
strlcpy(Settings.cors_domain, CORS_DOMAIN, sizeof(Settings.cors_domain));
// Button
// Settings.flag.button_restrict = 0;
@ -1176,6 +1181,13 @@ void SettingsDelta(void)
if (Settings.version < 0x07010202) {
Settings.serial_config = TS_SERIAL_8N1;
}
if (Settings.version < 0x07010204) {
if (Settings.flag3.ex_cors_enabled == 1) {
strlcpy(Settings.cors_domain, CORS_ENABLED_ALL, sizeof(Settings.cors_domain));
} else {
Settings.cors_domain[0] = 0;
}
}
Settings.version = VERSION;
SettingsSave(1);

View File

@ -51,7 +51,7 @@ void OsWatchTicker(void)
uint32_t last_run = abs(t - oswatch_last_loop_time);
#ifdef DEBUG_THEO
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d, last_run %d"), ESP.getFreeHeap(), WifiGetRssiAsQuality(WiFi.RSSI()), last_run);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d %% (%d dBm), last_run %d"), ESP.getFreeHeap(), WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI(), last_run);
#endif // DEBUG_THEO
if (last_run >= (OSWATCH_RESET_TIME * 1000)) {
// AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_OSWATCH " " D_BLOCKED_LOOP ". " D_RESTARTING)); // Save iram space
@ -107,12 +107,6 @@ String GetResetReason(void)
}
}
String GetResetReasonInfo(void)
{
// "Fatal exception:0 flag:2 (EXCEPTION) epc1:0x704022a7 epc2:0x00000000 epc3:0x00000000 excvaddr:0x00000000 depc:0x00000000"
return (ResetReason() == REASON_EXCEPTION_RST) ? ESP.getResetInfo() : GetResetReason();
}
/*********************************************************************************************\
* Miscellaneous
\*********************************************************************************************/
@ -383,6 +377,23 @@ char* Trim(char* p)
return p;
}
char* RemoveAllSpaces(char* p)
{
// remove any white space from the base64
char *cursor = p;
uint32_t offset = 0;
while (1) {
*cursor = *(cursor + offset);
if ((' ' == *cursor) || ('\t' == *cursor) || ('\n' == *cursor)) { // if space found, remove this char until end of string
offset++;
} else {
if (0 == *cursor) { break; }
cursor++;
}
}
return p;
}
char* NoAlNumToUnderscore(char* dest, const char* source)
{
char* write = dest;

View File

@ -334,6 +334,9 @@ void CmndStatus(void)
if ((!Settings.flag.mqtt_enabled) && (6 == payload)) { payload = 99; } // SetOption3 - Enable MQTT
if (!energy_flg && (9 == payload)) { payload = 99; }
bool exception_flg = (ResetReason() == REASON_EXCEPTION_RST);
if (!exception_flg && (12 == payload)) { payload = 99; }
if ((0 == payload) || (99 == payload)) {
uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present;
#ifdef USE_SONOFF_IFAN
@ -369,7 +372,7 @@ void CmndStatus(void)
D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_JSON_STARTUPUTC "\":\"%s\",\"" D_CMND_SLEEP "\":%d,\""
D_JSON_CONFIG_HOLDER "\":%d,\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_JSON_SAVEADDRESS "\":\"%X\"}}"),
baudrate, Settings.mqtt_grptopic, Settings.ota_url,
GetResetReasonInfo().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings.sleep,
GetResetReason().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings.sleep,
Settings.cfg_holder, Settings.bootcount, Settings.save_flag, GetSettingsAddress());
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "1"));
}
@ -377,9 +380,12 @@ void CmndStatus(void)
if ((0 == payload) || (2 == payload)) {
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS2_FIRMWARE "\":{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\""
D_JSON_BOOTVERSION "\":%d,\"" D_JSON_COREVERSION "\":\"" ARDUINO_ESP8266_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\","
"\"Hardware\":\"%s\"}}"),
"\"Hardware\":\"%s\""
"%s}}"),
my_version, my_image, GetBuildDateAndTime().c_str(),
ESP.getBootVersion(), ESP.getSdkVersion(), GetDeviceHardware().c_str());
ESP.getBootVersion(), ESP.getSdkVersion(),
GetDeviceHardware().c_str(),
GetStatistics().c_str());
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "2"));
}
@ -483,6 +489,15 @@ void CmndStatus(void)
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "11"));
}
if (exception_flg) {
if ((0 == payload) || (12 == payload)) {
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS12_STATUS "\":"));
CrashDump();
ResponseJsonEnd();
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "12"));
}
}
#ifdef USE_SCRIPT_STATUS
if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">U",2,mqtt_data);
#endif
@ -564,6 +579,9 @@ void CmndRestart(void)
restart_flag = 2;
ResponseCmndChar(D_JSON_RESTARTING);
break;
case -1:
CmndCrash(); // force a crash
break;
case 99:
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING));
EspRestart();

View File

@ -0,0 +1,89 @@
/*
support_crash_recorder.ino - record the call stack in RTC in case of crash
Copyright (C) 2019 Stephan Hadinger, Theo Arends,
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
const uint32_t crash_magic = 0x53415400; // Stack trace magic number (TASx)
const uint32_t crash_rtc_offset = 32; // Offset in RTC memory skipping OTA used block
const uint32_t crash_dump_max_len = 31; // Dump only 31 call addresses to satisfy max JSON length of about 600 characters
/**
* Save crash information in RTC memory
* This function is called automatically if ESP8266 suffers an exception
* It should be kept quick / consise to be able to execute before hardware wdt may kick in
*/
extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end )
{
uint32_t addr_written = 0; // how many addresses have we already written in RTC
uint32_t value; // 4 bytes buffer to write to RTC
for (uint32_t i = stack; i < stack_end; i += 4) {
value = *((uint32_t*) i); // load value from stack
if ((value >= 0x40000000) && (value < 0x40300000)) { // keep only addresses in code area
ESP.rtcUserMemoryWrite(crash_rtc_offset + addr_written, (uint32_t*)&value, sizeof(value));
addr_written++;
if (addr_written >= crash_dump_max_len) { break; } // we store only 31 addresses
}
}
value = crash_magic + addr_written;
ESP.rtcUserMemoryWrite(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value));
}
// Generate a crash to test the crash recorder
void CmndCrash(void)
{
volatile uint32_t dummy;
dummy = *((uint32_t*) 0x00000000);
}
// Clear the RTC dump counter when we do a normal reboot, this avoids garbage data to stay in RTC
void CrashDumpClear(void)
{
uint32_t value = 0;
ESP.rtcUserMemoryWrite(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value));
}
/*********************************************************************************************\
* CmndCrashDump - dump the crash history - called by `Status 12`
\*********************************************************************************************/
void CrashDump(void)
{
ResponseAppend_P(PSTR("{\"Exception\":%d,\"Reason\":\"%s\",\"EPC\":[\"%08x\",\"%08x\",\"%08x\"],\"EXCVADDR\":\"%08x\",\"DEPC\":\"%08x\""),
resetInfo.exccause, // Exception Cause
GetResetReason().c_str(), // Reset Reason
resetInfo.epc1, // Exception Progam Counter
resetInfo.epc2, // Exception Progam Counter - High-Priority Interrupt 1
resetInfo.epc3, // Exception Progam Counter - High-Priority Interrupt 2
resetInfo.excvaddr, // Exception Virtual Address Register - Virtual address that caused last fetch, load, or store exception
resetInfo.depc); // Double Exception Program Counter
uint32_t value;
ESP.rtcUserMemoryRead(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value));
if (crash_magic == (value & 0xFFFFFF00)) {
ResponseAppend_P(PSTR(",\"CallChain\":["));
uint32_t count = value & 0x3F;
for (uint32_t i = 0; i < count; i++) {
ESP.rtcUserMemoryRead(crash_rtc_offset +i, (uint32_t*)&value, sizeof(value));
if (i > 0) { ResponseAppend_P(PSTR(",")); }
ResponseAppend_P(PSTR("\"%08x\""), value);
}
ResponseAppend_P(PSTR("]"));
}
ResponseJsonEnd();
}

View File

@ -0,0 +1,88 @@
/*
support_statistics.ino - gather statistics for Tasmota
Copyright (C) 2019 Theo Arends
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define USE_STATS_CODE
#ifdef USE_STATS_CODE
/*********************************************************************************************\
* Gather statistics
\*********************************************************************************************/
struct STATS {
// uint32_t str_size = 1151; // Total number of characters reserved as char array in Settings
uint32_t str_len = 0; // Total number of characters used within char array
uint32_t str_char = 0; // Total number of character '|' within all char arrays
} Stats;
void StatisticsChar(const char* text)
{
uint32_t len = strlen(text);
Stats.str_len += len;
for (uint32_t i = 0; i < len; i++) {
if ('|' == text[i]) { // Text string separator as currently used in GetTextIndexed()
Stats.str_char++;
}
}
}
String GetStatistics(void)
{
for (uint32_t i = 0; i < 2; i++) {
StatisticsChar(Settings.sta_ssid[i]);
StatisticsChar(Settings.sta_pwd[i]);
}
for (uint32_t i = 0; i < 3; i++) {
StatisticsChar(Settings.mqtt_prefix[i]);
StatisticsChar(Settings.ntp_server[i]);
}
for (uint32_t i = 0; i < 4; i++) {
StatisticsChar(Settings.state_text[i]);
StatisticsChar(Settings.friendlyname[i]);
}
for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) {
StatisticsChar(Settings.mems[i]);
}
StatisticsChar(Settings.ota_url);
StatisticsChar(Settings.hostname);
StatisticsChar(Settings.syslog_host);
StatisticsChar(Settings.mqtt_host);
StatisticsChar(Settings.mqtt_client);
StatisticsChar(Settings.mqtt_user);
StatisticsChar(Settings.mqtt_pwd);
StatisticsChar(Settings.mqtt_topic);
StatisticsChar(Settings.button_topic);
StatisticsChar(Settings.switch_topic);
StatisticsChar(Settings.mqtt_grptopic);
StatisticsChar(Settings.web_password);
StatisticsChar(Settings.mqtt_fulltopic);
StatisticsChar(Settings.cors_domain);
char data[40];
snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/1151/%d\""), Stats.str_len, Stats.str_char); // Char Usage Ratio
return String(data);
}
#else
String GetStatistics(void)
{
return String("");
}
#endif // USE_STATS_CODE

View File

@ -565,7 +565,7 @@ void MqttShowState(void)
if (i == LightDevice()) { LightState(1); } // call it only once
} else {
#endif
ResponseAppend_P(PSTR(",\"%s\":{\"STATE\":\"%s\"}"), GetPowerDevice(stemp1, i, sizeof(stemp1), Settings.flag.device_index_enable), // SetOption26 - Switch between POWER or POWER1
ResponseAppend_P(PSTR(",\"%s\":\"%s\""), GetPowerDevice(stemp1, i, sizeof(stemp1), Settings.flag.device_index_enable), // SetOption26 - Switch between POWER or POWER1
GetStateText(bitRead(power, i-1)));
#ifdef USE_SONOFF_IFAN
if (IsModuleIfan()) {
@ -583,8 +583,9 @@ void MqttShowState(void)
MqttShowPWMState();
}
ResponseAppend_P(PSTR(",\"" D_JSON_WIFI "\":{\"" D_JSON_AP "\":%d,\"" D_JSON_SSID "\":\"%s\",\"" D_JSON_BSSID "\":\"%s\",\"" D_JSON_CHANNEL "\":%d,\"" D_JSON_RSSI "\":%d,\"" D_JSON_LINK_COUNT "\":%d,\"" D_JSON_DOWNTIME "\":\"%s\"}}"),
Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WiFi.BSSIDstr().c_str(), WiFi.channel(), WifiGetRssiAsQuality(WiFi.RSSI()), WifiLinkCount(), WifiDowntime().c_str());
ResponseAppend_P(PSTR(",\"" D_JSON_WIFI "\":{\"" D_JSON_AP "\":%d,\"" D_JSON_SSID "\":\"%s\",\"" D_JSON_BSSID "\":\"%s\",\"" D_JSON_CHANNEL "\":%d,\"" D_JSON_RSSI "\":%d,\"" D_JSON_SIGNAL "\":%d,\"" D_JSON_LINK_COUNT "\":%d,\"" D_JSON_DOWNTIME "\":\"%s\"}}"),
Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WiFi.BSSIDstr().c_str(), WiFi.channel(),
WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI(), WifiLinkCount(), WifiDowntime().c_str());
}
void MqttPublishTeleState(void)
@ -609,7 +610,7 @@ bool MqttShowSensor(void)
if (pin[GPIO_SWT1 +i] < 99) {
#endif // USE_TM1638
bool swm = ((FOLLOW_INV == Settings.switchmode[i]) || (PUSHBUTTON_INV == Settings.switchmode[i]) || (PUSHBUTTONHOLD_INV == Settings.switchmode[i]));
ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":{\"STATE\":\"%s\"}"), i +1, GetStateText(swm ^ SwitchLastState(i)));
ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":\"%s\""), i +1, GetStateText(swm ^ SwitchLastState(i)));
}
}
XsnsCall(FUNC_JSON_APPEND);
@ -1285,15 +1286,19 @@ void GpioInit(void)
}
#endif // USE_SONOFF_SC
if (!light_type) {
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
if (pin[GPIO_PWM1 +i] < 99) {
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
if (pin[GPIO_PWM1 +i] < 99) {
pinMode(pin[GPIO_PWM1 +i], OUTPUT);
if (light_type) {
// force PWM GPIOs to low or high mode, see #7165
analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range : 0);
} else {
pwm_present = true;
pinMode(pin[GPIO_PWM1 +i], OUTPUT);
analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]);
}
}
}
for (uint32_t i = 0; i < MAX_RELAYS; i++) {
if (pin[GPIO_REL1 +i] < 99) {
pinMode(pin[GPIO_REL1 +i], OUTPUT);

View File

@ -617,6 +617,7 @@ void WifiShutdown(void)
void EspRestart(void)
{
WifiShutdown();
CrashDumpClear(); // Clear the stack dump in RTC
// ESP.restart(); // This results in exception 3 on restarts on core 2.3.0
ESP.reset();
}

View File

@ -131,7 +131,7 @@ const uint32_t SOFT_BAUDRATE = 9600; // Default software serial baudrate
const uint32_t APP_BAUDRATE = 115200; // Default serial baudrate
const uint32_t SERIAL_POLLING = 100; // Serial receive polling in ms
const uint32_t ZIGBEE_POLLING = 100; // Serial receive polling in ms
const uint8_t MAX_STATUS = 11; // Max number of status lines
const uint8_t MAX_STATUS = 12; // Max number of status lines
const uint32_t START_VALID_TIME = 1451602800; // Time is synced and after 2016-01-01

View File

@ -20,6 +20,6 @@
#ifndef _TASMOTA_VERSION_H_
#define _TASMOTA_VERSION_H_
const uint32_t VERSION = 0x07010202;
const uint32_t VERSION = 0x07010204;
#endif // _TASMOTA_VERSION_H_

View File

@ -414,7 +414,8 @@ const char HTTP_FORM_WIFI[] PROGMEM =
"<p><b>" D_AP1_PASSWORD "</b><input type='checkbox' onclick='sp(\"p1\")'><br><input id='p1' type='password' placeholder='" D_AP1_PASSWORD "' value='" D_ASTERISK_PWD "'></p>"
"<p><b>" D_AP2_SSID "</b> (" STA_SSID2 ")<br><input id='s2' placeholder='" STA_SSID2 "' value='%s'></p>"
"<p><b>" D_AP2_PASSWORD "</b><input type='checkbox' onclick='sp(\"p2\")'><br><input id='p2' type='password' placeholder='" D_AP2_PASSWORD "' value='" D_ASTERISK_PWD "'></p>"
"<p><b>" D_HOSTNAME "</b> (%s)<br><input id='h' placeholder='%s' value='%s'></p>";
"<p><b>" D_HOSTNAME "</b> (%s)<br><input id='h' placeholder='%s' value='%s'></p>"
"<p><b>" D_CORS_DOMAIN "</b><input id='c' placeholder='" CORS_DOMAIN "' value='%s'></p>";
const char HTTP_FORM_LOG1[] PROGMEM =
"<fieldset><legend><b>&nbsp;" D_LOGGING_PARAMETERS "&nbsp;</b>"
@ -673,8 +674,8 @@ bool HttpCheckPriviledgedAccess(bool autorequestauth = true)
void HttpHeaderCors(void)
{
if (Settings.flag3.cors_enabled) { // SetOption73 - Enable HTTP CORS
WebServer->sendHeader(F("Access-Control-Allow-Origin"), F("*"));
if (Settings.cors_domain[0] != 0) {
WebServer->sendHeader(F("Access-Control-Allow-Origin"), Settings.cors_domain);
}
}
@ -1106,6 +1107,21 @@ void HandleRoot(void)
} else {
#endif // USE_SONOFF_IFAN
for (uint32_t idx = 1; idx <= devices_present; idx++) {
#ifdef USE_SHUTTER
if (Settings.flag3.shutter_mode) { // SetOption80 - Enable shutter support
bool shutter_used = false;
for (uint32_t i = 0; i < MAX_SHUTTERS; i++) {
if (Settings.shutter_startrelay[i] == (((idx -1) & 0xFFFFFFFE) +1)) {
shutter_used = true;
break;
}
}
if (shutter_used) {
WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, (idx % 2) ? "" : "" , "");
continue;
}
}
#endif // USE_SHUTTER
snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx);
WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, (devices_present < 5) ? D_BUTTON_TOGGLE : "", (devices_present > 1) ? stemp : "");
}
@ -1638,11 +1654,11 @@ void HandleWifiConfiguration(void)
int quality = WifiGetRssiAsQuality(WiFi.RSSI(indices[i]));
int auth = WiFi.encryptionType(indices[i]);
char encryption[20];
WSContentSend_P(PSTR("<div><a href='#p' onclick='c(this)'>%s</a>&nbsp;(%d)&nbsp<span class='q'>%s %d%%</span></div>"),
WSContentSend_P(PSTR("<div><a href='#p' onclick='c(this)'>%s</a>&nbsp;(%d)&nbsp<span class='q'>%s %d%% (%d dBm)</span></div>"),
HtmlEscape(WiFi.SSID(indices[i])).c_str(),
WiFi.channel(indices[i]),
GetTextIndexed(encryption, sizeof(encryption), auth +1, kEncryptionType),
quality
quality, WiFi.RSSI()
);
delay(0);
@ -1654,7 +1670,7 @@ void HandleWifiConfiguration(void)
}
// As WIFI_HOSTNAME may contain %s-%04d it cannot be part of HTTP_FORM_WIFI where it will exception
WSContentSend_P(HTTP_FORM_WIFI, Settings.sta_ssid[0], Settings.sta_ssid[1], WIFI_HOSTNAME, WIFI_HOSTNAME, Settings.hostname);
WSContentSend_P(HTTP_FORM_WIFI, Settings.sta_ssid[0], Settings.sta_ssid[1], WIFI_HOSTNAME, WIFI_HOSTNAME, Settings.hostname, Settings.cors_domain);
WSContentSend_P(HTTP_FORM_END);
}
@ -1678,6 +1694,8 @@ void WifiSaveSettings(void)
if (strstr(Settings.hostname, "%") != nullptr) {
strlcpy(Settings.hostname, WIFI_HOSTNAME, sizeof(Settings.hostname));
}
WebGetArg("c", tmp, sizeof(tmp));
strlcpy(Settings.cors_domain, (!strlen(tmp)) ? CORS_DOMAIN : tmp, sizeof(Settings.cors_domain));
WebGetArg("s1", tmp, sizeof(tmp));
strlcpy(Settings.sta_ssid[0], (!strlen(tmp)) ? STA_SSID1 : tmp, sizeof(Settings.sta_ssid[0]));
WebGetArg("s2", tmp, sizeof(tmp));
@ -1686,7 +1704,7 @@ void WifiSaveSettings(void)
strlcpy(Settings.sta_pwd[0], (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? Settings.sta_pwd[0] : tmp, sizeof(Settings.sta_pwd[0]));
WebGetArg("p2", tmp, sizeof(tmp));
strlcpy(Settings.sta_pwd[1], (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? Settings.sta_pwd[1] : tmp, sizeof(Settings.sta_pwd[1]));
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s"), Settings.hostname, Settings.sta_ssid[0], Settings.sta_ssid[1]);
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s, " D_CMND_CORS " %s"), Settings.hostname, Settings.sta_ssid[0], Settings.sta_ssid[1], Settings.cors_domain);
}
/*-------------------------------------------------------------------------------------------*/
@ -1970,7 +1988,7 @@ void HandleInformation(void)
WSContentSend_P(PSTR("}1" D_FRIENDLY_NAME " %d}2%s"), i +1, Settings.friendlyname[i]);
}
WSContentSend_P(PSTR("}1}2&nbsp;")); // Empty line
WSContentSend_P(PSTR("}1" D_AP "%d " D_SSID " (" D_RSSI ")}2%s (%d%%)"), Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WifiGetRssiAsQuality(WiFi.RSSI()));
WSContentSend_P(PSTR("}1" D_AP "%d " D_SSID " (" D_RSSI ")}2%s (%d%%, %d dBm)"), Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI());
WSContentSend_P(PSTR("}1" D_HOSTNAME "}2%s%s"), my_hostname, (Wifi.mdns_begun) ? ".local" : "");
if (static_cast<uint32_t>(WiFi.localIP()) != 0) {
WSContentSend_P(PSTR("}1" D_IP_ADDRESS "}2%s"), WiFi.localIP().toString().c_str());
@ -2698,7 +2716,7 @@ const char kWebCommands[] PROGMEM = "|" // No prefix
#ifdef USE_SENDMAIL
D_CMND_SENDMAIL "|"
#endif
D_CMND_WEBSERVER "|" D_CMND_WEBPASSWORD "|" D_CMND_WEBLOG "|" D_CMND_WEBREFRESH "|" D_CMND_WEBSEND "|" D_CMND_WEBCOLOR "|" D_CMND_WEBSENSOR;
D_CMND_WEBSERVER "|" D_CMND_WEBPASSWORD "|" D_CMND_WEBLOG "|" D_CMND_WEBREFRESH "|" D_CMND_WEBSEND "|" D_CMND_WEBCOLOR "|" D_CMND_WEBSENSOR "|" D_CMND_CORS;
void (* const WebCommand[])(void) PROGMEM = {
#ifdef USE_EMULATION
@ -2707,7 +2725,7 @@ void (* const WebCommand[])(void) PROGMEM = {
#ifdef USE_SENDMAIL
&CmndSendmail,
#endif
&CmndWebServer, &CmndWebPassword, &CmndWeblog, &CmndWebRefresh, &CmndWebSend, &CmndWebColor, &CmndWebSensor };
&CmndWebServer, &CmndWebPassword, &CmndWeblog, &CmndWebRefresh, &CmndWebSend, &CmndWebColor, &CmndWebSensor, &CmndCors };
/*********************************************************************************************\
* Commands
@ -2828,6 +2846,14 @@ void CmndWebSensor(void)
ResponseJsonEnd();
}
void CmndCors(void)
{
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.cors_domain))) {
strlcpy(Settings.cors_domain, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data, sizeof(Settings.cors_domain));
}
ResponseCmndChar(Settings.cors_domain);
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/

View File

@ -555,7 +555,13 @@ void MqttConnected(void)
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2"));
}
#endif // USE_WEBSERVER
Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":\"%s\"}"), GetResetReasonInfo().c_str());
Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":"));
if (ResetReason() == REASON_EXCEPTION_RST) {
CrashDump();
} else {
ResponseAppend_P(PSTR("\"%s\""), GetResetReason().c_str());
}
ResponseJsonEnd();
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3"));
MqttPublishAllPowerState();
if (Settings.tele_period) {
@ -1071,6 +1077,9 @@ void CmndTlsKey(void) {
}
memcpy_P(spi_buffer, tls_spi_start, tls_spi_len);
// remove any white space from the base64
RemoveAllSpaces(XdrvMailbox.data);
// allocate buffer for decoded base64
uint32_t bin_len = decode_base64_length((unsigned char*)XdrvMailbox.data);
uint8_t *bin_buf = nullptr;

View File

@ -554,10 +554,10 @@ const char HTTP_TIMER_SCRIPT2[] PROGMEM =
"o=qs('#ho');"
"e=o.childElementCount;"
"if(b==1){"
"qs('#dr').disabled='';"
"qs('#dr').style.visibility='';"
"if(e>12){for(i=12;i<=23;i++){o.removeChild(o.lastElementChild);}}" // Create offset hours select options
"}else{"
"qs('#dr').disabled='disabled';"
"qs('#dr').style.visibility='hidden';"
"if(e<23){for(i=12;i<=23;i++){ce(i,o);}}" // Create hours select options
"}"
"}";
@ -583,7 +583,7 @@ const char HTTP_TIMER_SCRIPT3[] PROGMEM =
"if(m==0){s|=l;}" // Get time
#ifdef USE_SUNRISE
"if((m==1)||(m==2)){"
"if(qs('#dr').selectedIndex>0){l+=720;}" // If negative offset, add 12h to given offset time
"if(qs('#dr').selectedIndex>0){if(l>0){l+=720;}}" // If negative offset and delta-time > 0, add 12h to given offset time
"s|=l&0x7FF;" // Save offset instead of time
"}"
#endif

View File

@ -438,7 +438,7 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
String ucommand = commands;
ucommand.toUpperCase();
// if (!ucommand.startsWith("BACKLOG")) { commands = "backlog " + commands; } // Always use Backlog to prevent power race exception
if (ucommand.indexOf("EVENT ") != -1) { commands = "backlog " + commands; } // Always use Backlog with event to prevent rule event loop exception
if ((ucommand.indexOf("EVENT ") != -1) && (ucommand.indexOf("BACKLOG ") == -1)) { commands = "backlog " + commands; } // Always use Backlog with event to prevent rule event loop exception
RulesVarReplace(commands, F("%VALUE%"), Rules.event_value);
for (uint32_t i = 0; i < MAX_RULE_VARS; i++) {

View File

@ -86,6 +86,7 @@ const char HASS_DISCOVER_SENSOR[] PROGMEM =
"{\"name\":\"%s\"," // dualr2 1 BTN
"\"stat_t\":\"%s\"," // cmnd/dualr2/POWER (implies "\"optimistic\":\"false\",")
"\"avty_t\":\"%s\"," // tele/dualr2/LWT
"\"frc_upd\":true," // force update for better graph representation
"\"pl_avail\":\"" D_ONLINE "\"," // Online
"\"pl_not_avail\":\"" D_OFFLINE "\""; // Offline

View File

@ -194,10 +194,10 @@ void ShutterInit(void)
}
} else {
Shutter.mode = SHT_OFF_ON__OPEN_CLOSE;
if (pin[GPIO_PWM1 ]+i < 99) {
if (pin[GPIO_PWM1+i] < 99) {
Shutter.pwm_frequency = 0;
analogWriteFreq(Shutter.pwm_frequency);
analogWrite(pin[GPIO_PWM1]+i, 50);
analogWrite(pin[GPIO_PWM1+i], 50);
}
}
@ -257,11 +257,11 @@ void ShutterUpdatePosition(void)
// Counter should be initiated to 0 to count movement.
// 0..1000 in step 100 = 10 steps with 0.05 sec = 0.5sec total ramp time from start to
// full speed.
if (pin[GPIO_PWM1]+i < 99 && Shutter.pwm_frequency != Shutter.max_pwm_frequency) {
if (pin[GPIO_PWM1+i] < 99 && Shutter.pwm_frequency != Shutter.max_pwm_frequency) {
Shutter.pwm_frequency += Shutter.max_pwm_frequency/20;
Shutter.pwm_frequency = (Shutter.pwm_frequency > Shutter.max_pwm_frequency ? Shutter.max_pwm_frequency : Shutter.pwm_frequency);
analogWriteFreq(Shutter.pwm_frequency);
analogWrite(pin[GPIO_PWM1]+i, 50);
analogWrite(pin[GPIO_PWM1+i], 50);
}
Shutter.real_position[i] = Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i]));
@ -287,21 +287,21 @@ void ShutterUpdatePosition(void)
// This is a failsafe configuration. Relay1 ON/OFF Relay2 -1/1 direction
// Only allow PWM microstepping if PWM and COUNTER are defined.
// see wiki to connect PWM and COUNTER
if (pin[GPIO_PWM1 ]+i < 99 && pin[GPIO_CNTR1 ]+i < 99 ) {
if (pin[GPIO_PWM1+i] < 99 && pin[GPIO_CNTR1+i] < 99 ) {
int16_t missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) - RtcSettings.pulse_counter[i];
Shutter.pwm_frequency = 0;
//slow down for acurate position
analogWriteFreq(500);
analogWrite(pin[GPIO_PWM1]+i, 50);
analogWrite(pin[GPIO_PWM1+i], 50);
//prepare for stop PWM
Shutter.motordelay[i] = -2 + Shutter.motordelay[i] + missing_steps/(Shutter.max_pwm_frequency/20);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Missing steps %d, adjust motordelay %d, counter %d, temp realpos %d"), missing_steps, Shutter.motordelay[i],RtcSettings.pulse_counter[i] ,Shutter.real_position[i]);
Settings.shutter_motordelay[i]=Shutter.motordelay[i];
Settings.shutter_motordelay[i]=(missing_steps > 0 ? Shutter.motordelay[i] : 0);
analogWriteFreq(0);
while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) {
delay(1);
}
analogWrite(pin[GPIO_PWM1]+i, 0);
analogWrite(pin[GPIO_PWM1+i], 0);
Shutter.real_position[i] = ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency)+Shutter.start_position[i];
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT:Realpos %d, pulsecount %d, startpos %d, int32 %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i], ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency));
@ -357,12 +357,12 @@ void ShutterStartInit(uint8_t index, uint8_t direction, int32_t target_pos)
Shutter.target_position[index] = target_pos;
Shutter.start_position[index] = Shutter.real_position[index];
Shutter.time[index] = 0;
if (pin[GPIO_PWM1]+index < 99) {
if (pin[GPIO_PWM1+index] < 99) {
Shutter.pwm_frequency = 0;
analogWriteFreq(Shutter.pwm_frequency);
analogWrite(pin[GPIO_PWM1]+index, 0);
analogWrite(pin[GPIO_PWM1+index], 0);
// can be operated without counter, but then not that acurate.
if (pin[GPIO_CNTR1]+index < 99) {
if (pin[GPIO_CNTR1+index] < 99) {
RtcSettings.pulse_counter[index] = 0;
}
}
@ -459,6 +459,10 @@ void ShutterSetPosition(uint8_t device, uint8_t position)
void CmndShutterOpen(void)
{
//AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Payload close: %d, index %d"), XdrvMailbox.payload, XdrvMailbox.index);
if ( XdrvMailbox.index == 1 && XdrvMailbox.payload != -99) {
XdrvMailbox.index = XdrvMailbox.payload;
}
XdrvMailbox.payload = 100;
last_source = SRC_WEBGUI;
CmndShutterPosition();
@ -466,6 +470,10 @@ void CmndShutterOpen(void)
void CmndShutterClose(void)
{
//AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Payload open: %d, index %d"), XdrvMailbox.payload, XdrvMailbox.index);
if ( XdrvMailbox.index == 1 && XdrvMailbox.payload != -99) {
XdrvMailbox.index = XdrvMailbox.payload;
}
XdrvMailbox.payload = 0;
XdrvMailbox.data_len = 0;
last_source = SRC_WEBGUI;
@ -475,6 +483,9 @@ void CmndShutterClose(void)
void CmndShutterStop(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
if ( XdrvMailbox.index == 1 && XdrvMailbox.payload != -99) {
XdrvMailbox.index = XdrvMailbox.payload;
}
uint32_t index = XdrvMailbox.index -1;
if (Shutter.direction[index] != 0) {

View File

@ -28,7 +28,7 @@
#define XSNS_06 6
#define DHT_MAX_SENSORS 3
#define DHT_MAX_SENSORS 4
#define DHT_MAX_RETRY 8
uint32_t dht_max_cycles;

View File

@ -57,6 +57,7 @@ int16_t MPU_6050_temperature = 0;
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
VectorFloat gravity; // [x, y, z] gravity vector
float euler[3]; // [psi, theta, phi] Euler angle container
float yawPitchRoll[3]; // [yaw, pitch roll] Yaw-pitch-roll container
} MPU6050_DMP;
MPU6050_DMP MPU6050_dmp;
@ -68,7 +69,7 @@ MPU6050 mpu6050;
void MPU_6050PerformReading(void)
{
#ifdef USE_MPU6050_DMP
mpu6050.resetFIFO(); // with a default dampling rate of 200Hz, we create a delay of approx. 5ms with a complete read cycle
mpu6050.resetFIFO(); // with a default sampling rate of 200Hz, we create a delay of approx. 5ms with a complete read cycle
MPU6050_dmp.fifoCount = mpu6050.getFIFOCount();
while (MPU6050_dmp.fifoCount < MPU6050_dmp.packetSize) MPU6050_dmp.fifoCount = mpu6050.getFIFOCount();
mpu6050.getFIFOBytes(MPU6050_dmp.fifoBuffer, MPU6050_dmp.packetSize);
@ -79,6 +80,7 @@ void MPU_6050PerformReading(void)
mpu6050.dmpGetAccel(&MPU6050_dmp.aa, MPU6050_dmp.fifoBuffer);
mpu6050.dmpGetGravity(&MPU6050_dmp.gravity, &MPU6050_dmp.q);
mpu6050.dmpGetLinearAccel(&MPU6050_dmp.aaReal, &MPU6050_dmp.aa, &MPU6050_dmp.gravity);
mpu6050.dmpGetYawPitchRoll(MPU6050_dmp.yawPitchRoll, &MPU6050_dmp.q, &MPU6050_dmp.gravity);
MPU_6050_gx = MPU6050_dmp.euler[0] * 180/M_PI;
MPU_6050_gy = MPU6050_dmp.euler[1] * 180/M_PI;
MPU_6050_gz = MPU6050_dmp.euler[2] * 180/M_PI;
@ -145,6 +147,10 @@ void MPU_6050Detect(void)
}
}
#define D_YAW "Yaw"
#define D_PITCH "Pitch"
#define D_ROLL "Roll"
#ifdef USE_WEBSERVER
const char HTTP_SNS_AXIS[] PROGMEM =
"{s}" D_SENSOR_MPU6050 " " D_AX_AXIS "{m}%s{e}" // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
@ -153,6 +159,12 @@ const char HTTP_SNS_AXIS[] PROGMEM =
"{s}" D_SENSOR_MPU6050 " " D_GX_AXIS "{m}%s{e}" // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
"{s}" D_SENSOR_MPU6050 " " D_GY_AXIS "{m}%s{e}" // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
"{s}" D_SENSOR_MPU6050 " " D_GZ_AXIS "{m}%s{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
#ifdef USE_MPU6050_DMP
const char HTTP_SNS_YPR[] PROGMEM =
"{s}" D_SENSOR_MPU6050 " " D_YAW "{m}%s{e}" // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
"{s}" D_SENSOR_MPU6050 " " D_PITCH "{m}%s{e}" // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
"{s}" D_SENSOR_MPU6050 " " D_ROLL "{m}%s{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
#endif // USE_MPU6050_DMP
#endif // USE_WEBSERVER
#define D_JSON_AXIS_AX "AccelXAxis"
@ -161,6 +173,9 @@ const char HTTP_SNS_AXIS[] PROGMEM =
#define D_JSON_AXIS_GX "GyroXAxis"
#define D_JSON_AXIS_GY "GyroYAxis"
#define D_JSON_AXIS_GZ "GyroZAxis"
#define D_JSON_YAW "Yaw"
#define D_JSON_PITCH "Pitch"
#define D_JSON_ROLL "Roll"
void MPU_6050Show(bool json)
{
@ -181,6 +196,14 @@ void MPU_6050Show(bool json)
dtostrfd(MPU_6050_gy, Settings.flag2.axis_resolution, axis_gy);
char axis_gz[33];
dtostrfd(MPU_6050_gz, Settings.flag2.axis_resolution, axis_gz);
#ifdef USE_MPU6050_DMP
char axis_yaw[33];
dtostrfd(MPU6050_dmp.yawPitchRoll[0] / PI * 180.0, Settings.flag2.axis_resolution, axis_yaw);
char axis_pitch[33];
dtostrfd(MPU6050_dmp.yawPitchRoll[1] / PI * 180.0, Settings.flag2.axis_resolution, axis_pitch);
char axis_roll[33];
dtostrfd(MPU6050_dmp.yawPitchRoll[2] / PI * 180.0, Settings.flag2.axis_resolution, axis_roll);
#endif // USE_MPU6050_DMP
if (json) {
char json_axis_ax[25];
@ -195,8 +218,20 @@ void MPU_6050Show(bool json)
snprintf_P(json_axis_gy, sizeof(json_axis_gy), PSTR(",\"" D_JSON_AXIS_GY "\":%s"), axis_gy);
char json_axis_gz[25];
snprintf_P(json_axis_gz, sizeof(json_axis_gz), PSTR(",\"" D_JSON_AXIS_GZ "\":%s"), axis_gz);
#ifdef USE_MPU6050_DMP
char json_ypr_y[25];
snprintf_P(json_ypr_y, sizeof(json_ypr_y), PSTR(",\"" D_JSON_YAW "\":%s"), axis_yaw);
char json_ypr_p[25];
snprintf_P(json_ypr_p, sizeof(json_ypr_p), PSTR(",\"" D_JSON_PITCH "\":%s"), axis_pitch);
char json_ypr_r[25];
snprintf_P(json_ypr_r, sizeof(json_ypr_r), PSTR(",\"" D_JSON_ROLL "\":%s"), axis_roll);
ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s%s%s%s}"),
D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz,
json_ypr_y, json_ypr_p, json_ypr_r);
#else
ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s}"),
D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz);
#endif // USE_MPU6050_DMP
#ifdef USE_DOMOTICZ
DomoticzSensor(DZ_TEMP, temperature);
#endif // USE_DOMOTICZ
@ -204,6 +239,9 @@ void MPU_6050Show(bool json)
} else {
WSContentSend_PD(HTTP_SNS_TEMP, D_SENSOR_MPU6050, temperature, TempUnit());
WSContentSend_PD(HTTP_SNS_AXIS, axis_ax, axis_ay, axis_az, axis_gx, axis_gy, axis_gz);
#ifdef USE_MPU6050_DMP
WSContentSend_PD(HTTP_SNS_YPR, axis_yaw, axis_pitch, axis_roll);
#endif // USE_MPU6050_DMP
#endif // USE_WEBSERVER
}
}

View File

@ -57,6 +57,7 @@
#define D_JSON_WEIGHT_MAX "WeightMax"
#define D_JSON_WEIGHT_ITEM "WeightItem"
#define D_JSON_WEIGHT_CHANGE "WeightChange"
#define D_JSON_WEIGHT_RAW "WeightRaw"
enum HxCalibrationSteps { HX_CAL_END, HX_CAL_LIMBO, HX_CAL_FINISH, HX_CAL_FAIL, HX_CAL_DONE, HX_CAL_FIRST, HX_CAL_RESET, HX_CAL_START };
@ -64,8 +65,10 @@ const char kHxCalibrationStates[] PROGMEM = D_HX_CAL_FAIL "|" D_HX_CAL_DONE "|"
struct HX {
long weight = 0;
long raw = 0;
long last_weight = 0;
long sum_weight = 0;
long sum_raw = 0;
long offset = 0;
long scale = 1;
long weight_diff = 0;
@ -272,13 +275,17 @@ void HxInit(void)
void HxEvery100mSecond(void)
{
Hx.sum_weight += HxRead();
long raw = HxRead();
Hx.sum_raw += raw;
Hx.sum_weight += raw;
Hx.sample_count++;
if (HX_SAMPLES == Hx.sample_count) {
long average = Hx.sum_weight / Hx.sample_count; // grams
long raw_average = Hx.sum_raw / Hx.sample_count; // grams
long value = average - Hx.offset; // grams
Hx.weight = value / Hx.scale; // grams
Hx.raw = raw_average / Hx.scale;
if (Hx.weight < 0) {
if (Settings.energy_frequency_calibration) {
long difference = Settings.energy_frequency_calibration + Hx.weight;
@ -367,6 +374,7 @@ void HxEvery100mSecond(void)
}
Hx.sum_weight = 0;
Hx.sum_raw = 0;
Hx.sample_count = 0;
}
}
@ -405,7 +413,7 @@ void HxShow(bool json)
dtostrfd(weight, Settings.flag2.weight_resolution, weight_chr);
if (json) {
ResponseAppend_P(PSTR(",\"HX711\":{\"" D_JSON_WEIGHT "\":%s%s}"), weight_chr, scount);
ResponseAppend_P(PSTR(",\"HX711\":{\"" D_JSON_WEIGHT "\":%s%s, \"" D_JSON_WEIGHT_RAW "\":%d}"), weight_chr, scount, Hx.raw);
#ifdef USE_WEBSERVER
} else {
WSContentSend_PD(HTTP_HX711_WEIGHT, weight_chr);