diff --git a/README.md b/README.md index 9fd6e0505..f55adc2c6 100644 --- a/README.md +++ b/README.md @@ -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 **5.1.0** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/_releasenotes.ino) for change information. +Current version is **5.1.1** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/_releasenotes.ino) for change information. ### **** ATTENTION Version 5.x.x specific information **** @@ -44,5 +44,9 @@ The following devices are supported: - [iTead Motor Clockwise/Anticlockwise](https://www.itead.cc/smart-home/motor-reversing-wifi-wireless-switch.html) - [Electrodragon IoT Relay Board](http://www.electrodragon.com/product/wifi-iot-relay-board-based-esp8266/) +Future support +- [iTead Sonoff 4CH Pro](https://www.itead.cc/sonoff-4ch-pro.html) + + diff --git a/api/arduino/sonoff.ino.bin b/api/arduino/sonoff.ino.bin index f47b5cf84..f2bbac47b 100644 Binary files a/api/arduino/sonoff.ino.bin and b/api/arduino/sonoff.ino.bin differ diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index 449599b52..8cd202877 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,4 +1,12 @@ -/* 5.1.0 20170513 +/* 5.1.1 20170517 + * Allow command FullTopic in group mode + * Prepare for more use of RTC memory + * Add independant WS2812 led string power control (#386, #390) + * Add command Counter to control up to four GPIO falling edge interrupt counters or timers (#459) + * Add command CounterType to select between pulse counting or pulse timing + * Add command CounterDebounce to select global counter debounce time in mSec + * + * 5.1.0 20170513 * Fix Offline/Removal of retained topic when FullTopic is changed * Add FullTopic to MQTT Configuration and Information web pages * Add license model GPLv3 (#188) diff --git a/sonoff/settings.h b/sonoff/settings.h index 832a09491..4b719efe1 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -196,14 +196,20 @@ struct SYSCFG { // 5.0.4a char mqtt_fulltopic[101]; + // 5.1.1 + unsigned long pCounter[4]; + uint16_t pCounterType; + uint16_t pCounterDebounce; + } sysCfg; struct RTCMEM { uint16_t valid; byte osw_flag; - byte nu1; + uint8_t power; unsigned long hlw_kWhtoday; unsigned long hlw_kWhtotal; + unsigned long pCounter[4]; } rtcMem; // See issue https://github.com/esp8266/Arduino/issues/2913 diff --git a/sonoff/settings.ino b/sonoff/settings.ino index e24c74866..5b52b7daa 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -59,6 +59,12 @@ void RTC_Load() if (rtcMem.valid != RTC_MEM_VALID) { memset(&rtcMem, 0x00, sizeof(RTCMEM)); rtcMem.valid = RTC_MEM_VALID; + rtcMem.power = sysCfg.power; + rtcMem.hlw_kWhtoday = sysCfg.hlw_kWhtoday; + rtcMem.hlw_kWhtotal = sysCfg.hlw_kWhtotal; + for (byte i = 0; i < 4; i++) { + rtcMem.pCounter[i] = sysCfg.pCounter[i]; + } RTC_Save(); } _rtcHash = getRtcHash(); @@ -633,6 +639,14 @@ void CFG_Delta() if (sysCfg.version < 0x05000600) { sysCfg.mqtt_retry = MQTT_RETRY_SECS; } + if (sysCfg.version < 0x05010100) { + sysCfg.pCounterType = 0; + sysCfg.pCounterDebounce = 0; + for (byte i = 0; i < 4; i++) { + sysCfg.pCounter[i] = 0; + rtcMem.pCounter[i] = 0; + } + } sysCfg.version = VERSION; } } diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 929b8dca2..454db32ae 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -16,18 +16,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ - -/* - ==================================================== +/*==================================================== Prerequisites: - Change libraries/PubSubClient/src/PubSubClient.h #define MQTT_MAX_PACKET_SIZE 512 - Select IDE Tools - Flash size: "1M (no SPIFFS)" - ==================================================== -*/ + ====================================================*/ -#define VERSION 0x05010000 // 5.1.0 +#define VERSION 0x05010100 // 5.1.1 enum log_t {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE, LOG_LEVEL_ALL}; enum week_t {Last, First, Second, Third, Fourth}; @@ -254,6 +251,7 @@ uint8_t blink_powersave; // Blink start power save state uint16_t mqtt_cmnd_publish = 0; // ignore flag for publish command uint8_t latching_power = 0; // Power state at latching start uint8_t latching_relay_pulse = 0; // Latching relay pulse timer +unsigned long pTimeLast[4]; // Last counter time in milli seconds #ifdef USE_MQTT_TLS WiFiClientSecure espClient; // Wifi Secure Client @@ -706,7 +704,7 @@ boolean mqtt_command(boolean grpflg, char *type, uint16_t index, char *dataBuf, } snprintf_P(svalue, ssvalue, PSTR("{\"MqttPassword\":\"%s\"}"), sysCfg.mqtt_pwd); } - else if (!grpflg && !strcmp_P(type,PSTR("FULLTOPIC"))) { + else if (!strcmp_P(type,PSTR("FULLTOPIC"))) { if ((data_len > 0) && (data_len < sizeof(sysCfg.mqtt_fulltopic))) { for (i = 0; i <= data_len; i++) { if ((dataBuf[i] == '+') || (dataBuf[i] == '#') || (dataBuf[i] == ' ')) { @@ -1213,6 +1211,27 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) } snprintf_P(svalue, sizeof(svalue), PSTR("%s}}"),svalue); } + else if (!strcmp_P(type,PSTR("COUNTER")) && (index > 0) && (index <= 4)) { + if ((data_len > 0) && (pin[GPIO_CNTR1 + index -1] < 99)) { + rtcMem.pCounter[index -1] = payload16; + sysCfg.pCounter[index -1] = payload16; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"Counter%d\":%d}"), index, rtcMem.pCounter[index -1]); + } + else if (!strcmp_P(type,PSTR("COUNTERTYPE")) && (index > 0) && (index <= 4)) { + if ((data_len > 0) && (payload >= 0) && (payload <= 1) && (pin[GPIO_CNTR1 + index -1] < 99)) { + bitWrite(sysCfg.pCounterType, index -1, payload &1); + rtcMem.pCounter[index -1] = 0; + sysCfg.pCounter[index -1] = 0; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"CounterType%d\":%d}"), index, bitRead(sysCfg.pCounterType, index -1)); + } + else if (!strcmp_P(type,PSTR("COUNTERDEBOUNCE"))) { + if ((data_len > 0) && (payload16 < 32001) && (pin[GPIO_CNTR1 + index -1] < 99)) { + sysCfg.pCounterDebounce = payload16; + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"CounterDebounce\":%d}"), sysCfg.pCounterDebounce); + } else if (!strcmp_P(type,PSTR("SLEEP"))) { if ((data_len > 0) && (payload >= 0) && (payload < 251)) { if ((!sysCfg.sleep && payload) || (sysCfg.sleep && !payload)) { @@ -1527,7 +1546,9 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) snprintf_P(svalue, sizeof(svalue), PSTR("{\"Command\":\"Unknown\"}")); type = (char*)topicBuf; } - mqtt_publish_topic_P(5, type, svalue); + if (svalue[0] != '\0') { + mqtt_publish_topic_P(5, type, svalue); + } } /********************************************************************************************/ @@ -1786,6 +1807,12 @@ void sensors_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson) *djson = 1; } } + for (byte i = 0; i < 4; i++) { + if (pin[GPIO_CNTR1 +i] < 99) { + snprintf_P(svalue, ssvalue, PSTR("%s, {\"Counter%d\":%d}"), svalue, i +1, rtcMem.pCounter[i]); + *djson = 1; + } + } #ifndef USE_ADC_VCC if (pin[GPIO_ADC0] < 99) { snprintf_P(svalue, ssvalue, PSTR("%s, \"AnalogInput0\":%d"), svalue, analogRead(A0)); @@ -2187,6 +2214,9 @@ void stateloop() } break; case (STATES/10)*4: + if (rtc_midnight_now()) { + counter_savestate(); + } if (savedatacounter) { savedatacounter--; if (savedatacounter <= 0) { @@ -2221,6 +2251,7 @@ void stateloop() if (hlw_flg) { hlw_savestate(); } + counter_savestate(); CFG_Save(); restartflag--; if (restartflag <= 0) { @@ -2436,7 +2467,8 @@ void GPIO_init() analogWrite(pin[GPIO_PWM1 +i], sysCfg.pwmvalue[i]); } } - + counter_init(); + if (EXS_RELAY == sysCfg.module) { setLatchingRelay(0,2); setLatchingRelay(1,2); @@ -2469,7 +2501,8 @@ void GPIO_init() #ifdef USE_WS2812 if (pin[GPIO_WS2812] < 99) { - ws2812_init(); + Maxdevice++; + ws2812_init(Maxdevice); } #endif // USE_WS2812 diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index ebad89b65..ece8c1c1c 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -57,6 +57,10 @@ enum upins_t { GPIO_PWM3, // Red (swapped with Blue from original) GPIO_PWM4, // Green GPIO_PWM5, // Blue (swapped with Red from original) + GPIO_CNTR1, + GPIO_CNTR2, + GPIO_CNTR3, + GPIO_CNTR4, GPIO_SENSOR_END }; // Text in webpage Module Parameters and commands GPIOS and GPIO @@ -98,7 +102,11 @@ const char sensors[GPIO_SENSOR_END][9] PROGMEM = { "PWM2", "PWM3", "PWM4", - "PWM5" + "PWM5", + "Counter1", + "Counter2", + "Counter3", + "Counter4" }; // Programmer selectable GPIO functionality offset by user selectable GPIOs diff --git a/sonoff/support.ino b/sonoff/support.ino index abd826b58..01acad6eb 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -682,6 +682,7 @@ uint32_t dsttime = 0; uint32_t stdtime = 0; uint32_t ntptime = 0; uint32_t midnight = 1451602800; +uint8_t midnightnow = 0; String getBuildDateTime() { @@ -867,6 +868,15 @@ uint32_t rtc_midnight() return midnight; } +boolean rtc_midnight_now() +{ + boolean mnflg = midnightnow; + if (mnflg) { + midnightnow = 0; + } + return mnflg; +} + void rtc_second() { char log[LOGSZ]; @@ -919,6 +929,7 @@ void rtc_second() breakTime(loctime, rtcTime); if (!rtcTime.Hour && !rtcTime.Minute && !rtcTime.Second && rtcTime.Valid) { midnight = loctime; + midnightnow = 1; } rtcTime.Year += 1970; } @@ -936,6 +947,70 @@ void rtc_init() tickerRTC.attach(1, rtc_second); } +/*********************************************************************************************\ + * Counter sensors (water meters, electricity meters etc.) +\*********************************************************************************************/ + +void counter_update(byte index) +{ +// char log[LOGSZ]; + + unsigned long pTime = millis() - pTimeLast[index -1]; + if (pTime > sysCfg.pCounterDebounce) { + pTimeLast[index -1] = millis(); + if (bitRead(sysCfg.pCounterType, index -1)) { + rtcMem.pCounter[index -1] = pTime; + } else { + rtcMem.pCounter[index -1]++; + } + +// snprintf_P(log, sizeof(log), PSTR("CNTR: Interrupt %d"), index); +// addLog(LOG_LEVEL_DEBUG, log); + } +} + +void counter_update1() +{ + counter_update(1); +} + +void counter_update2() +{ + counter_update(2); +} + +void counter_update3() +{ + counter_update(3); +} + +void counter_update4() +{ + counter_update(4); +} + +void counter_savestate() +{ + for (byte i = 0; i < 4; i++) { + if (pin[GPIO_CNTR1 +i] < 99) { + sysCfg.pCounter[i] = rtcMem.pCounter[i]; + } + } +} + +void counter_init() +{ + typedef void (*function) () ; + function counter_callbacks[] = { counter_update1, counter_update2, counter_update3, counter_update4 }; + + for (byte i = 0; i < 4; i++) { + if (pin[GPIO_CNTR1 +i] < 99) { + pinMode(pin[GPIO_CNTR1 +i], INPUT_PULLUP); + attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], FALLING); + } + } +} + /*********************************************************************************************\ * Miscellaneous \*********************************************************************************************/ diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 6207269e2..10833a396 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -165,7 +165,7 @@ #define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+8k code, +1k mem) - Disable by // #define USE_WS2812_CTYPE 1 // WS2812 Color type (0 - RGB, 1 - GRB) -// #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial TXD) (+1k mem) +// #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial RXD) (+1k mem) // When USE_WS2812_DMA is enabled expect Exceptions on Pow /*********************************************************************************************\ diff --git a/sonoff/webserver.ino b/sonoff/webserver.ino index d054b9185..960a7e5ef 100644 --- a/sonoff/webserver.ino +++ b/sonoff/webserver.ino @@ -250,6 +250,8 @@ const char HTTP_TABLE100[] PROGMEM = ""; const char HTTP_COUNTER[] PROGMEM = "
"; +const char HTTP_SNS_COUNTER[] PROGMEM = + ""; const char HTTP_SNS_TEMP[] PROGMEM = ""; const char HTTP_SNS_HUM[] PROGMEM = @@ -447,7 +449,7 @@ void handleRoot() void handleAjax2() { - char svalue[16]; + char svalue[80]; if (strlen(webServer->arg("o").c_str())) { do_cmnd_power(atoi(webServer->arg("o").c_str()), 2); @@ -458,6 +460,12 @@ void handleAjax2() } String tpage = ""; + for (byte i = 0; i < 4; i++) { + if (pin[GPIO_CNTR1 +i] < 99) { + snprintf_P(svalue, sizeof(svalue), HTTP_SNS_COUNTER, i+1, rtcMem.pCounter[i]); + tpage += svalue; + } + } if (hlw_flg) { tpage += hlw_webPresent(); } diff --git a/sonoff/xdrv_wemohue.ino b/sonoff/xdrv_wemohue.ino index 559eb1588..720f1e138 100644 --- a/sonoff/xdrv_wemohue.ino +++ b/sonoff/xdrv_wemohue.ino @@ -346,10 +346,12 @@ void handleUPnPevent() String request = webServer->arg(0); if (request.indexOf("State>1 0) { - do_cmnd_power(1, 1); +// do_cmnd_power(1, 1); + do_cmnd_power(Maxdevice, 1); } if (request.indexOf("State>0 0) { - do_cmnd_power(1, 0); +// do_cmnd_power(1, 0); + do_cmnd_power(Maxdevice, 0); } webServer->send(200, F("text/plain"), ""); } diff --git a/sonoff/xdrv_ws2812.ino b/sonoff/xdrv_ws2812.ino index 6598c5398..5989984d8 100644 --- a/sonoff/xdrv_ws2812.ino +++ b/sonoff/xdrv_ws2812.ino @@ -107,6 +107,7 @@ RgbColor tcolor; RgbColor lcolor; uint8_t wakeupDimmer = 0; +uint8_t ws_bit = 0; uint16_t wakeupCntr = 0; unsigned long stripTimerCntr = 0; // Bars and Gradient @@ -394,7 +395,7 @@ void ws2812_animate() uint8_t fadeValue; stripTimerCntr++; - if (0 == power) { // Power Off + if (0 == bitRead(power, ws_bit)) { // Power Off sleep = sysCfg.sleep; stripTimerCntr = 0; tcolor = 0; @@ -464,7 +465,7 @@ void ws2812_animate() } } - if ((sysCfg.ws_scheme <= 1) || (!(power &1))) { + if ((sysCfg.ws_scheme <= 1) || (0 == bitRead(power, ws_bit))) { if ((lcolor != tcolor) || lany) { lany = 0; lcolor = tcolor; @@ -499,8 +500,9 @@ void ws2812_pixels() lany = 1; } -void ws2812_init() +void ws2812_init(uint8_t powerbit) { + ws_bit = powerbit -1; #ifdef USE_WS2812_DMA #if (USE_WS2812_CTYPE == 1) strip = new NeoPixelBus(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. @@ -544,14 +546,14 @@ boolean ws2812_command(char *type, uint16_t index, char *dataBuf, uint16_t data_ if (6 == data_len) { // ws2812_setColor(0, dataBufUc); ws2812_setColor(0, dataBuf); - power = 1; + bitSet(power, ws_bit); } ws2812_getColor(0, svalue, ssvalue); } else if (!strcmp_P(type,PSTR("DIMMER"))) { if ((data_len > 0) && (payload >= 0) && (payload <= 100)) { sysCfg.ws_dimmer = payload; - power = 1; + bitSet(power, ws_bit); #ifdef USE_DOMOTICZ mqtt_publishDomoticzPowerState(index); #endif // USE_DOMOTICZ @@ -599,6 +601,23 @@ boolean ws2812_command(char *type, uint16_t index, char *dataBuf, uint16_t data_ } snprintf_P(svalue, ssvalue, PSTR("{\"Width\":%d}"), sysCfg.ws_width); } + else if (!strcmp_P(type,PSTR("UNDOCA"))) { // Theos WS2812 legacy status + RgbColor mcolor; + char mtopic[TOPSZ]; + getTopic_P(mtopic, 1, sysCfg.mqtt_topic, type); + ws2812_setDim(sysCfg.ws_dimmer); + mcolor = dcolor; + uint32_t color = (uint32_t)mcolor.R << 16; + color += (uint32_t)mcolor.G << 8; + color += (uint32_t)mcolor.B; + snprintf_P(svalue, ssvalue, PSTR("%s, %s, %d, %d, %d, %06X, %d, 0, %d, %d, %d, %d, %d, %d"), + Version, sysCfg.mqtt_topic, bitRead(power, ws_bit), sysCfg.ws_fade, sysCfg.ws_dimmer, color, + sysCfg.ws_pixels, sysCfg.ws_wakeup, + sysCfg.ws_scheme, sysCfg.ws_speed, sysCfg.ws_width, sysCfg.timezone, sysCfg.ws_ledtable); + mqtt_publish(mtopic, svalue); +// snprintf_P(svalue, ssvalue, PSTR("{\"UndocA\":\"Done\"}")); + svalue[0] = '\0'; + } else if (!strcmp_P(type,PSTR("WAKEUP"))) { if ((data_len > 0) && (payload > 0) && (payload < 3601)) { sysCfg.ws_wakeup = payload; @@ -614,7 +633,7 @@ boolean ws2812_command(char *type, uint16_t index, char *dataBuf, uint16_t data_ if (1 == sysCfg.ws_scheme) { ws2812_resetWakupState(); } - power = 1; + bitSet(power, ws_bit); ws2812_resetStripTimer(); } snprintf_P(svalue, ssvalue, PSTR("{\"Scheme\":%d}"), sysCfg.ws_scheme); diff --git a/sonoff/xsns_hlw8012.ino b/sonoff/xsns_hlw8012.ino index dacc79a8d..6d840ba3d 100644 --- a/sonoff/xsns_hlw8012.ino +++ b/sonoff/xsns_hlw8012.ino @@ -265,9 +265,6 @@ void hlw_init() hlw_Ecntr = 0; hlw_EDcntr = 0; hlw_kWhtoday = (RTC_Valid()) ? rtcMem.hlw_kWhtoday : 0; - if (sysCfg.hlw_kWhtotal > rtcMem.hlw_kWhtotal) { - rtcMem.hlw_kWhtotal = sysCfg.hlw_kWhtotal; - } hlw_SELflag = 0; // Voltage;
Counter%d%d
%s Temperature%s°%c