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<x> to control up to four GPIO falling edge
interrupt counters or timers (#459)
* Add command CounterType<x> to select between pulse counting or pulse
timing
* Add command CounterDebounce to select global counter debounce time in
mSec
This commit is contained in:
arendst 2017-05-17 22:49:22 +02:00
parent afc7b96b7c
commit 88648a8099
13 changed files with 201 additions and 27 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 **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)
<img src="https://github.com/arendst/arendst.github.io/blob/master/media/sonofftoucheu.jpg" height="280" align="left" />
<img src="https://github.com/arendst/arendst.github.io/blob/master/media/sonoff4ch.jpg" height="250" align="right" />

Binary file not shown.

View File

@ -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<x> to control up to four GPIO falling edge interrupt counters or timers (#459)
* Add command CounterType<x> 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)

View File

@ -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

View File

@ -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;
}
}

View File

@ -16,18 +16,15 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
====================================================
/*====================================================
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

View File

@ -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

View File

@ -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
\*********************************************************************************************/

View File

@ -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
/*********************************************************************************************\

View File

@ -250,6 +250,8 @@ const char HTTP_TABLE100[] PROGMEM =
"<table style='width:100%'>";
const char HTTP_COUNTER[] PROGMEM =
"<br/><div id='t' name='t' style='text-align:center;'></div>";
const char HTTP_SNS_COUNTER[] PROGMEM =
"<tr><th>Counter%d</th><td>%d</td></tr>";
const char HTTP_SNS_TEMP[] PROGMEM =
"<tr><th>%s Temperature</th><td>%s&deg;%c</td></tr>";
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();
}

View File

@ -346,10 +346,12 @@ void handleUPnPevent()
String request = webServer->arg(0);
if (request.indexOf("State>1</Binary") > 0) {
do_cmnd_power(1, 1);
// do_cmnd_power(1, 1);
do_cmnd_power(Maxdevice, 1);
}
if (request.indexOf("State>0</Binary") > 0) {
do_cmnd_power(1, 0);
// do_cmnd_power(1, 0);
do_cmnd_power(Maxdevice, 0);
}
webServer->send(200, F("text/plain"), "");
}

View File

@ -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<NeoGrbFeature, Neo800KbpsMethod>(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);

View File

@ -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;