diff --git a/README.md b/README.md index 4ab1e7d3b..712d4a5fc 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 **4.0.6** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/_releasenotes.ino) for change information. +Current version is **4.0.7** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/master/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```. diff --git a/api/arduino/sonoff-minimal.ino.bin b/api/arduino/sonoff-minimal.ino.bin index b8e800cc4..84e7a4ba7 100644 Binary files a/api/arduino/sonoff-minimal.ino.bin and b/api/arduino/sonoff-minimal.ino.bin differ diff --git a/api/arduino/sonoff.ino.bin b/api/arduino/sonoff.ino.bin index b451f62d0..9709ac87c 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 667c91864..93b3152f8 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,4 +1,18 @@ -/* 4.0.6 20170316 +/* 4.0.7 20170319 + * Increased Sonoff Led PWM frequency from 432 to 1000 + * Fix possible watch dog reboot after changing module type on web page + * Fix reporting of GPIO usage from web page + * Fix Sonoff Led blank during firmware upgrade + * Fix Sonoff Led flicker and possible flash corruption by using latest Arduino-esp8266 versions + * of pwm core files included in sonoff library (#211) + * Add PWM output control with commands PWM1 to PWM5 using user selectable GPIOs (#211) + * Fix exceptions due to low values of commands HlwPCal (10000), HlwUCal (1000) and HlwICal (2500) (#223) + * Add Switch state to sensor status (#227, #233) + * Add user configuarble GPIO to module Sonoff Touch (#228) + * Add define WEB_PORT to user_config.h to change default web server port from 80 (#232) + * Fix failed Ota Firmware upgrade started from Web page (#235) + * + * 4.0.6 20170316 * Fix to better find device by Wifi hostname * Fix compile error when some I2C devices are disabled * Add (experimental) support for SHT1X emulating I2C (#97) diff --git a/sonoff/core_esp8266_timer.c b/sonoff/core_esp8266_timer.c new file mode 100644 index 000000000..1b18861ae --- /dev/null +++ b/sonoff/core_esp8266_timer.c @@ -0,0 +1,103 @@ +/* + timer.c - Timer1 library for esp8266 + + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "wiring_private.h" +#include "pins_arduino.h" + +#include "c_types.h" +#include "ets_sys.h" + +// ------------------------------------------------------------------ - +// timer 1 + +static volatile timercallback timer1_user_cb = NULL; + +void ICACHE_RAM_ATTR timer1_isr_handler(void *para){ + (void) para; + if ((T1C & ((1 << TCAR) | (1 << TCIT))) == 0) TEIE &= ~TEIE1;//edge int disable + T1I = 0; + if (timer1_user_cb) { + // to make ISR compatible to Arduino AVR model where interrupts are disabled + // we disable them before we call the client ISR + uint32_t savedPS = xt_rsil(15); // stop other interrupts + timer1_user_cb(); + xt_wsr_ps(savedPS); + } +} + +void ICACHE_RAM_ATTR timer1_isr_init(){ + ETS_FRC_TIMER1_INTR_ATTACH(timer1_isr_handler, NULL); +} + +void timer1_attachInterrupt(timercallback userFunc) { + timer1_user_cb = userFunc; + ETS_FRC1_INTR_ENABLE(); +} + +void ICACHE_RAM_ATTR timer1_detachInterrupt() { + timer1_user_cb = 0; + TEIE &= ~TEIE1;//edge int disable + ETS_FRC1_INTR_DISABLE(); +} + +void timer1_enable(uint8_t divider, uint8_t int_type, uint8_t reload){ + T1C = (1 << TCTE) | ((divider & 3) << TCPD) | ((int_type & 1) << TCIT) | ((reload & 1) << TCAR); + T1I = 0; +} + +void ICACHE_RAM_ATTR timer1_write(uint32_t ticks){ + T1L = ((ticks)& 0x7FFFFF); + if ((T1C & (1 << TCIT)) == 0) TEIE |= TEIE1;//edge int enable +} + +void ICACHE_RAM_ATTR timer1_disable(){ + T1C = 0; + T1I = 0; +} + +//------------------------------------------------------------------- +// timer 0 + +static volatile timercallback timer0_user_cb = NULL; + +void ICACHE_RAM_ATTR timer0_isr_handler(void* para){ + (void) para; + if (timer0_user_cb) { + // to make ISR compatible to Arduino AVR model where interrupts are disabled + // we disable them before we call the client ISR + uint32_t savedPS = xt_rsil(15); // stop other interrupts + timer0_user_cb(); + xt_wsr_ps(savedPS); + } +} + +void timer0_isr_init(){ + ETS_CCOMPARE0_INTR_ATTACH(timer0_isr_handler, NULL); +} + +void timer0_attachInterrupt(timercallback userFunc) { + timer0_user_cb = userFunc; + ETS_CCOMPARE0_ENABLE(); +} + +void ICACHE_RAM_ATTR timer0_detachInterrupt() { + timer0_user_cb = NULL; + ETS_CCOMPARE0_DISABLE(); +} diff --git a/sonoff/core_esp8266_wiring_digital.c b/sonoff/core_esp8266_wiring_digital.c new file mode 100644 index 000000000..3e304b864 --- /dev/null +++ b/sonoff/core_esp8266_wiring_digital.c @@ -0,0 +1,188 @@ +/* + digital.c - wiring digital implementation for esp8266 + + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#define ARDUINO_MAIN +#include "wiring_private.h" +#include "pins_arduino.h" +#include "c_types.h" +#include "eagle_soc.h" +#include "ets_sys.h" + +extern void pwm_stop_pin(uint8_t pin); + +uint8_t esp8266_gpioToFn[16] = {0x34, 0x18, 0x38, 0x14, 0x3C, 0x40, 0x1C, 0x20, 0x24, 0x28, 0x2C, 0x30, 0x04, 0x08, 0x0C, 0x10}; + +extern void __pinMode(uint8_t pin, uint8_t mode) { + pwm_stop_pin(pin); + if(pin < 16){ + if(mode == SPECIAL){ + GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) + GPEC = (1 << pin); //Disable + GPF(pin) = GPFFS(GPFFS_BUS(pin));//Set mode to BUS (RX0, TX0, TX1, SPI, HSPI or CLK depending in the pin) + if(pin == 3) GPF(pin) |= (1 << GPFPU);//enable pullup on RX + } else if(mode & FUNCTION_0){ + GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) + GPEC = (1 << pin); //Disable + GPF(pin) = GPFFS((mode >> 4) & 0x07); + if(pin == 13 && mode == FUNCTION_4) GPF(pin) |= (1 << GPFPU);//enable pullup on RX + } else if(mode == OUTPUT || mode == OUTPUT_OPEN_DRAIN){ + GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO + GPC(pin) = (GPC(pin) & (0xF << GPCI)); //SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) + if(mode == OUTPUT_OPEN_DRAIN) GPC(pin) |= (1 << GPCD); + GPES = (1 << pin); //Enable + } else if(mode == INPUT || mode == INPUT_PULLUP){ + GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO + GPEC = (1 << pin); //Disable + GPC(pin) = (GPC(pin) & (0xF << GPCI)) | (1 << GPCD); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED) + if(mode == INPUT_PULLUP) { + GPF(pin) |= (1 << GPFPU); // Enable Pullup + } + } else if(mode == WAKEUP_PULLUP || mode == WAKEUP_PULLDOWN){ + GPF(pin) = GPFFS(GPFFS_GPIO(pin));//Set mode to GPIO + GPEC = (1 << pin); //Disable + if(mode == WAKEUP_PULLUP) { + GPF(pin) |= (1 << GPFPU); // Enable Pullup + GPC(pin) = (1 << GPCD) | (4 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(LOW) | WAKEUP_ENABLE(ENABLED) + } else { + GPF(pin) |= (1 << GPFPD); // Enable Pulldown + GPC(pin) = (1 << GPCD) | (5 << GPCI) | (1 << GPCWE); //SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(HIGH) | WAKEUP_ENABLE(ENABLED) + } + } + } else if(pin == 16){ + GPF16 = GP16FFS(GPFFS_GPIO(pin));//Set mode to GPIO + GPC16 = 0; + if(mode == INPUT || mode == INPUT_PULLDOWN_16){ + if(mode == INPUT_PULLDOWN_16){ + GPF16 |= (1 << GP16FPD);//Enable Pulldown + } + GP16E &= ~1; + } else if(mode == OUTPUT){ + GP16E |= 1; + } + } +} + +extern void ICACHE_RAM_ATTR __digitalWrite(uint8_t pin, uint8_t val) { + pwm_stop_pin(pin); + if(pin < 16){ + if(val) GPOS = (1 << pin); + else GPOC = (1 << pin); + } else if(pin == 16){ + if(val) GP16O |= 1; + else GP16O &= ~1; + } +} + +extern int ICACHE_RAM_ATTR __digitalRead(uint8_t pin) { + pwm_stop_pin(pin); + if(pin < 16){ + return GPIP(pin); + } else if(pin == 16){ + return GP16I & 0x01; + } + return 0; +} + +/* + GPIO INTERRUPTS +*/ + +typedef void (*voidFuncPtr)(void); + +typedef struct { + uint8_t mode; + void (*fn)(void); +} interrupt_handler_t; + +static interrupt_handler_t interrupt_handlers[16]; +static uint32_t interrupt_reg = 0; + +void ICACHE_RAM_ATTR interrupt_handler(void *arg) { + (void) arg; + uint32_t status = GPIE; + GPIEC = status;//clear them interrupts + uint32_t levels = GPI; + if(status == 0 || interrupt_reg == 0) return; + ETS_GPIO_INTR_DISABLE(); + int i = 0; + uint32_t changedbits = status & interrupt_reg; + while(changedbits){ + while(!(changedbits & (1 << i))) i++; + changedbits &= ~(1 << i); + interrupt_handler_t *handler = &interrupt_handlers[i]; + if (handler->fn && + (handler->mode == CHANGE || + (handler->mode & 1) == !!(levels & (1 << i)))) { + // to make ISR compatible to Arduino AVR model where interrupts are disabled + // we disable them before we call the client ISR + uint32_t savedPS = xt_rsil(15); // stop other interrupts + handler->fn(); + xt_wsr_ps(savedPS); + } + } + ETS_GPIO_INTR_ENABLE(); +} + +extern void ICACHE_RAM_ATTR __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int mode) { + if(pin < 16) { + interrupt_handler_t *handler = &interrupt_handlers[pin]; + handler->mode = mode; + handler->fn = userFunc; + interrupt_reg |= (1 << pin); + GPC(pin) &= ~(0xF << GPCI);//INT mode disabled + GPIEC = (1 << pin); //Clear Interrupt for this pin + GPC(pin) |= ((mode & 0xF) << GPCI);//INT mode "mode" + } +} + +extern void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin) { + if(pin < 16) { + GPC(pin) &= ~(0xF << GPCI);//INT mode disabled + GPIEC = (1 << pin); //Clear Interrupt for this pin + interrupt_reg &= ~(1 << pin); + interrupt_handler_t *handler = &interrupt_handlers[pin]; + handler->mode = 0; + handler->fn = 0; + } +} + +void initPins() { + //Disable UART interrupts + system_set_os_print(0); + U0IE = 0; + U1IE = 0; + + for (int i = 0; i <= 5; ++i) { + pinMode(i, INPUT); + } + // pins 6-11 are used for the SPI flash interface + for (int i = 12; i <= 16; ++i) { + pinMode(i, INPUT); + } + + ETS_GPIO_INTR_ATTACH(interrupt_handler, &interrupt_reg); + ETS_GPIO_INTR_ENABLE(); +} + +extern void pinMode(uint8_t pin, uint8_t mode) __attribute__ ((weak, alias("__pinMode"))); +extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__ ((weak, alias("__digitalWrite"))); +extern int digitalRead(uint8_t pin) __attribute__ ((weak, alias("__digitalRead"))); +extern void attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode) __attribute__ ((weak, alias("__attachInterrupt"))); +extern void detachInterrupt(uint8_t pin) __attribute__ ((weak, alias("__detachInterrupt"))); diff --git a/sonoff/core_esp8266_wiring_pwm.c b/sonoff/core_esp8266_wiring_pwm.c new file mode 100644 index 000000000..671eaa414 --- /dev/null +++ b/sonoff/core_esp8266_wiring_pwm.c @@ -0,0 +1,221 @@ +/* + pwm.c - analogWrite implementation for esp8266 + + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "wiring_private.h" +#include "pins_arduino.h" +#include "c_types.h" +#include "eagle_soc.h" +#include "ets_sys.h" + +#ifndef F_CPU +#define F_CPU 800000000L +#endif + +struct pwm_isr_table { + uint8_t len; + uint16_t steps[17]; + uint32_t masks[17]; +}; + +struct pwm_isr_data { + struct pwm_isr_table tables[2]; + uint8_t active;//0 or 1, which table is active in ISR +}; + +static struct pwm_isr_data _pwm_isr_data; + +uint32_t pwm_mask = 0; +uint16_t pwm_values[17] = {0,}; +uint32_t pwm_freq = 1000; +uint32_t pwm_range = PWMRANGE; + +uint8_t pwm_steps_changed = 0; +uint32_t pwm_multiplier = 0; + +int pwm_sort_array(uint16_t a[], uint16_t al) +{ + uint16_t i, j; + for (i = 1; i < al; i++) { + uint16_t tmp = a[i]; + for (j = i; j >= 1 && tmp < a[j-1]; j--) { + a[j] = a[j-1]; + } + a[j] = tmp; + } + int bl = 1; + for(i = 1; i < al; i++) { + if(a[i] != a[i-1]) { + a[bl++] = a[i]; + } + } + return bl; +} + +uint32_t pwm_get_mask(uint16_t value) +{ + uint32_t mask = 0; + int i; + for(i=0; i<17; i++) { + if((pwm_mask & (1 << i)) != 0 && pwm_values[i] == value) { + mask |= (1 << i); + } + } + return mask; +} + +void prep_pwm_steps() +{ + if(pwm_mask == 0) { + return; + } + + int pwm_temp_steps_len = 0; + uint16_t pwm_temp_steps[17]; + uint32_t pwm_temp_masks[17]; + uint32_t range = pwm_range; + + if((F_CPU / ESP8266_CLOCK) == 1) { + range /= 2; + } + + int i; + for(i=0; i<17; i++) { + if((pwm_mask & (1 << i)) != 0 && pwm_values[i] != 0) { + pwm_temp_steps[pwm_temp_steps_len++] = pwm_values[i]; + } + } + pwm_temp_steps[pwm_temp_steps_len++] = range; + pwm_temp_steps_len = pwm_sort_array(pwm_temp_steps, pwm_temp_steps_len) - 1; + for(i=0; i0; i--) { + pwm_temp_steps[i] = pwm_temp_steps[i] - pwm_temp_steps[i-1]; + } + + pwm_steps_changed = 0; + struct pwm_isr_table *table = &(_pwm_isr_data.tables[!_pwm_isr_data.active]); + table->len = pwm_temp_steps_len; + ets_memcpy(table->steps, pwm_temp_steps, (pwm_temp_steps_len + 1) * 2); + ets_memcpy(table->masks, pwm_temp_masks, pwm_temp_steps_len * 4); + pwm_multiplier = ESP8266_CLOCK/(range * pwm_freq); + pwm_steps_changed = 1; +} + +void ICACHE_RAM_ATTR pwm_timer_isr() //103-138 +{ + struct pwm_isr_table *table = &(_pwm_isr_data.tables[_pwm_isr_data.active]); + static uint8_t current_step = 0; + TEIE &= ~TEIE1;//14 + T1I = 0;//9 + if(current_step < table->len) { //20/21 + uint32_t mask = table->masks[current_step] & pwm_mask; + if(mask & 0xFFFF) { + GPOC = mask & 0xFFFF; //15/21 + } + if(mask & 0x10000) { + GP16O = 0; //6/13 + } + current_step++;//1 + } else { + current_step = 0;//1 + if(pwm_mask == 0) { //12 + table->len = 0; + return; + } + if(pwm_mask & 0xFFFF) { + GPOS = pwm_mask & 0xFFFF; //11 + } + if(pwm_mask & 0x10000) { + GP16O = 1; //5/13 + } + if(pwm_steps_changed) { //12/21 + _pwm_isr_data.active = !_pwm_isr_data.active; + table = &(_pwm_isr_data.tables[_pwm_isr_data.active]); + pwm_steps_changed = 0; + } + } + T1L = (table->steps[current_step] * pwm_multiplier);//23 + TEIE |= TEIE1;//13 +} + +void pwm_start_timer() +{ + timer1_disable(); + ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL); + ETS_FRC_TIMER1_NMI_INTR_ATTACH(pwm_timer_isr); + timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE); + timer1_write(1); +} + +void ICACHE_RAM_ATTR pwm_stop_pin(uint8_t pin) +{ + if(pwm_mask){ + pwm_mask &= ~(1 << pin); + if(pwm_mask == 0) { + ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL); + timer1_disable(); + timer1_isr_init(); + } + } +} + +extern void __analogWrite(uint8_t pin, int value) +{ + bool start_timer = false; + if(value == 0) { + digitalWrite(pin, LOW); + prep_pwm_steps(); + return; + } + if((pwm_mask & (1 << pin)) == 0) { + if(pwm_mask == 0) { + memset(&_pwm_isr_data, 0, sizeof(_pwm_isr_data)); + start_timer = true; + } + pinMode(pin, OUTPUT); + digitalWrite(pin, LOW); + pwm_mask |= (1 << pin); + } + if((F_CPU / ESP8266_CLOCK) == 1) { + value = (value+1) / 2; + } + pwm_values[pin] = value % (pwm_range + 1); + prep_pwm_steps(); + if(start_timer) { + pwm_start_timer(); + } +} + +extern void __analogWriteFreq(uint32_t freq) +{ + pwm_freq = freq; + prep_pwm_steps(); +} + +extern void __analogWriteRange(uint32_t range) +{ + pwm_range = range; + prep_pwm_steps(); +} + +extern void analogWrite(uint8_t pin, int val) __attribute__ ((weak, alias("__analogWrite"))); +extern void analogWriteFreq(uint32_t freq) __attribute__ ((weak, alias("__analogWriteFreq"))); +extern void analogWriteRange(uint32_t range) __attribute__ ((weak, alias("__analogWriteRange"))); diff --git a/sonoff/settings.h b/sonoff/settings.h index 0ce5c9dd4..f5a9f5b10 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -191,6 +191,7 @@ struct SYSCFG { char ntp_server[3][33]; uint16_t pulsetime[MAX_PULSETIMERS]; + uint16_t pwmvalue[5]; } sysCfg; diff --git a/sonoff/settings.ino b/sonoff/settings.ino index a4ce3d21f..ff2e148bd 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -198,7 +198,8 @@ void CFG_Save() } } else { #endif // USE_SPIFFS - if (sysCfg.module != SONOFF_LED) noInterrupts(); + noInterrupts(); +// if (sysCfg.module != SONOFF_LED) noInterrupts(); if (sysCfg.saveFlag == 0) { // Handle default and rollover spi_flash_erase_sector(CFG_LOCATION + (sysCfg.saveFlag &1)); spi_flash_write((CFG_LOCATION + (sysCfg.saveFlag &1)) * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg, sizeof(SYSCFG)); @@ -206,7 +207,8 @@ void CFG_Save() sysCfg.saveFlag++; spi_flash_erase_sector(CFG_LOCATION + (sysCfg.saveFlag &1)); spi_flash_write((CFG_LOCATION + (sysCfg.saveFlag &1)) * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg, sizeof(SYSCFG)); - if (sysCfg.module != SONOFF_LED) interrupts(); + interrupts(); +// if (sysCfg.module != SONOFF_LED) interrupts(); snprintf_P(log, sizeof(log), PSTR("Config: Saved configuration (%d bytes) to flash at %X and count %d"), sizeof(SYSCFG), CFG_LOCATION + (sysCfg.saveFlag &1), sysCfg.saveFlag); addLog(LOG_LEVEL_DEBUG, log); } @@ -511,6 +513,9 @@ void CFG_DefaultSet2() CFG_DefaultSet_4_0_4(); sysCfg.pulsetime[0] = APP_PULSETIME; + + // v4.0.7 + for (byte i = 0; i < 5; i++) sysCfg.pwmvalue[i] = 0; } void CFG_DefaultSet_3_2_4() @@ -738,6 +743,9 @@ void CFG_Delta() memmove(sysCfg.my_module.gp.io, sysCfg.my_module.gp.io +1, MAX_GPIO_PIN -1); // move myio 1 byte to front sysCfg.my_module.gp.io[MAX_GPIO_PIN -1] = 0; // Clear ADC0 } + if (sysCfg.version < 0x04000700) { + for (byte i = 0; i < 5; i++) sysCfg.pwmvalue[i] = 0; + } sysCfg.version = VERSION; } } diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index bd160a70f..279f41f4d 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -12,9 +12,9 @@ //#define ALLOW_MIGRATE_TO_V3 #ifdef ALLOW_MIGRATE_TO_V3 - #define VERSION 0x03091D00 // 3.9.29 + #define VERSION 0x03091E00 // 3.9.30 #else - #define VERSION 0x04000600 // 4.0.6 + #define VERSION 0x04000700 // 4.0.7 #endif // ALLOW_MIGRATE_TO_V3 enum log_t {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE, LOG_LEVEL_ALL}; @@ -115,6 +115,9 @@ enum emul_t {EMUL_NONE, EMUL_WEMO, EMUL_HUE, EMUL_MAX}; #define MAX_PULSETIMERS 4 // Max number of supported pulse timers #define WS2812_MAX_LEDS 256 // Max number of LEDs +#define PWM_RANGE 1023 // 127..1023 but as Color is addressed by 8 bits it should be 255 for my code +#define PWM_FREQ 1000 // 100..1000 Hz led refresh + #define MAX_POWER_HOLD 10 // Time in SECONDS to allow max agreed power (Pow) #define MAX_POWER_WINDOW 30 // Time in SECONDS to disable allow max agreed power (Pow) #define SAFE_POWER_HOLD 10 // Time in SECONDS to allow max unit safe power (Pow) @@ -284,6 +287,8 @@ uint8_t swt_flg = 0; // Any external switch configured uint8_t dht_type = 0; // DHT type (DHT11, DHT21 or DHT22) uint8_t hlw_flg = 0; // Power monitor configured uint8_t i2c_flg = 0; // I2C configured +uint8_t pwm_flg = 0; // PWM configured +uint8_t pwm_idxoffset = 0; // Allowed PWM command offset (change for Sonoff Led) boolean mDNSbegun = false; @@ -883,7 +888,10 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) } else if (!strcmp(type,"MODULE")) { if ((data_len > 0) && (payload > 0) && (payload <= MAXMODULE)) { - sysCfg.module = payload -1; + payload--; + byte new_modflg = (sysCfg.module != payload); + sysCfg.module = payload; + if (new_modflg) for (byte i = 0; i < MAX_GPIO_PIN; i++) sysCfg.my_module.gp.io[i] = 0; restartflag = 2; } snprintf_P(stemp1, sizeof(stemp1), modules[sysCfg.module].name); @@ -957,6 +965,21 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) } snprintf_P(svalue, sizeof(svalue), PSTR("%s\"}"), svalue); } + else if (!strcmp(type,"PWM") && (index > pwm_idxoffset) && (index <= 5)) { + if ((data_len > 0) && (payload >= 0) && (payload <= PWM_RANGE) && (pin[GPIO_PWM1 + index -1] < 99)) { + sysCfg.pwmvalue[index -1] = payload; + analogWrite(pin[GPIO_PWM1 + index -1], payload); + } + snprintf_P(svalue, sizeof(svalue), PSTR("{\"PWM\":{")); + bool first = true; + for (byte i = 0; i < 5; i++) { + if(pin[GPIO_PWM1 + i] < 99) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s\"PWM%d\":%d"), svalue, first ? "" : ", ", i+1, sysCfg.pwmvalue[i]); + first = false; + } + } + snprintf_P(svalue, sizeof(svalue), PSTR("%s}}"),svalue); + } else if (!strcmp(type,"SLEEP")) { if ((data_len > 0) && (payload >= 0) && (payload < 251)) { if ((!sysCfg.sleep && payload) || (sysCfg.sleep && !payload)) restartflag = 2; @@ -1247,6 +1270,7 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) snprintf_P(svalue, sizeof(svalue), PSTR("%s, Weblog, Webserver, WebPassword, Emulation"), svalue); #endif if (swt_flg) snprintf_P(svalue, sizeof(svalue), PSTR("%s, SwitchMode"), svalue); + if (pwm_flg) snprintf_P(svalue, sizeof(svalue), PSTR("%s, PWM"), svalue); #ifdef USE_I2C if (i2c_flg) snprintf_P(svalue, sizeof(svalue), PSTR("%s, I2CScan"), svalue); #endif // USE_I2C @@ -1508,7 +1532,13 @@ void state_mqttPresent(char* svalue, uint16_t ssvalue) void sensors_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson) { snprintf_P(svalue, ssvalue, PSTR("%s{\"Time\":\"%s\""), svalue, getDateTime().c_str()); - + for (byte i = 0; i < 4; i++) { + if (pin[GPIO_SWT1 +i] < 99) { + byte swm = ((sysCfg.switchmode[i] == FOLLOW_INV)||(sysCfg.switchmode[i] == PUSHBUTTON_INV)); + snprintf_P(svalue, ssvalue, PSTR("%s, \"Switch%d\":\"%s\""), + svalue, i +1, (lastwallswitch[i]) ? (swm) ? MQTT_STATUS_ON : MQTT_STATUS_OFF : (swm) ? MQTT_STATUS_OFF : MQTT_STATUS_ON); + } + } #ifndef USE_ADC_VCC if (pin[GPIO_ADC0] < 99) { snprintf_P(svalue, ssvalue, PSTR("%s, \"AnalogInput0\":%d"), svalue, analogRead(A0)); @@ -1999,6 +2029,9 @@ void GPIO_init() } } + analogWriteRange(PWM_RANGE); // Default is 1023 (Arduino.h) + analogWriteFreq(PWM_FREQ); // Default is 1000 (core_esp8266_wiring_pwm.c) + Maxdevice = 1; if (sysCfg.module == SONOFF_DUAL) { Maxdevice = 2; @@ -2009,6 +2042,7 @@ void GPIO_init() Baudrate = 19200; } else if (sysCfg.module == SONOFF_LED) { + pwm_idxoffset = 2; pin[GPIO_WS2812] = 99; // I do not allow both Sonoff Led AND WS2812 led sl_init(); } @@ -2033,6 +2067,14 @@ void GPIO_init() lastwallswitch[i] = digitalRead(pin[GPIO_SWT1 +i]); // set global now so doesn't change the saved power state on first switch check } } + for (byte i = pwm_idxoffset; i < 5; i++) { + if (pin[GPIO_PWM1 +i] < 99) { + pwm_flg = 1; + pinMode(pin[GPIO_PWM1 +i], OUTPUT); + analogWrite(pin[GPIO_PWM1 +i], sysCfg.pwmvalue[i]); + } + } + if (sysCfg.module == EXS_RELAY) { setLatchingRelay(0,2); setLatchingRelay(1,2); diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index aa5da9468..2c85c8d24 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -37,6 +37,11 @@ enum upins_t { GPIO_LED2_INV, GPIO_LED3_INV, GPIO_LED4_INV, + GPIO_PWM1, // Sonoff Led Cold + GPIO_PWM2, // Sonoff Led Warm + GPIO_PWM3, // Red (swapped with Blue from original) + GPIO_PWM4, // Green + GPIO_PWM5, // Blue (swapped with Red from original) GPIO_SENSOR_END }; // Text in webpage Module Parameters and commands GPIOS and GPIO @@ -73,17 +78,17 @@ const char sensors[GPIO_SENSOR_END][9] PROGMEM = { "Led1I", "Led2I", "Led3I", - "Led4I" + "Led4I", + "PWM1", + "PWM2", + "PWM3", + "PWM4", + "PWM5" }; // Programmer selectable GPIO functionality offset by user selectable GPIOs enum fpins_t { - GPIO_PWM0 = GPIO_SENSOR_END, // Cold - GPIO_PWM1, // Warm - GPIO_PWM2, // Red (swapped with Blue from original) - GPIO_PWM3, // Green - GPIO_PWM4, // Blue (swapped with Red from original) - GPIO_RXD, // Serial interface + GPIO_RXD = GPIO_SENSOR_END, // Serial interface GPIO_TXD, // Serial interface GPIO_HLW_SEL, // HLW8012 Sel output (Sonoff Pow) GPIO_HLW_CF1, // HLW8012 CF1 voltage / current (Sonoff Pow) @@ -250,7 +255,10 @@ const mytmplt modules[MAXMODULE] PROGMEM = { }, { "Sonoff Touch", // Sonoff Touch (ESP8285) GPIO_KEY1, // GPIO00 Button - 0, 0, 0, 0, 0, + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + 0, + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + 0, 0, 0, 0, 0, // Flash connection 0, 0, 0, GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) @@ -263,9 +271,9 @@ const mytmplt modules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO04 Optional sensor (PWM3 Green) GPIO_USER, // GPIO05 Optional sensor (PWM2 Red) 0, 0, 0, 0, 0, 0, // Flash connection - GPIO_PWM0, // GPIO12 Cold light + GPIO_PWM1, // GPIO12 Cold light (PWM0 Cold) GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - GPIO_PWM1, // GPIO14 Warm light + GPIO_PWM2, // GPIO14 Warm light (PWM1 Warm) GPIO_USER, // GPIO15 Optional sensor (PWM4 Blue) 0, 0 }, diff --git a/sonoff/support.ino b/sonoff/support.ino index b54c4c519..985e9d0eb 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -89,22 +89,22 @@ ctx: cont sp: 3fff1f30 end: 3fff2840 offset: 01a0 >>>stack>>> -3fff20d0: 202c3573 756f7247 2c302070 646e4920 -3fff20e0: 40236a6e 7954202c 45206570 00454358 -3fff20f0: 00000010 00000007 00000000 3fff2180 -3fff2100: 3fff2190 40107bfc 3fff3e4c 3fff22c0 -3fff2110: 40261934 000000f0 3fff22c0 401004d8 -3fff2120: 40238fcf 00000050 3fff2100 4021fc10 -3fff2130: 3fff32bc 4021680c 3ffeade1 4021ff7d -3fff2140: 3fff2190 3fff2180 0000000c 7fffffff -3fff2150: 00000019 00000000 00000000 3fff21c0 -3fff2160: 3fff23f3 3ffe8e08 00000000 4021ffb4 -3fff2170: 3fff2190 3fff2180 0000000c 40201118 -3fff2180: 3fff21c0 0000003c 3ffef840 00000007 -3fff2190: 00000000 00000000 00000000 40201128 -3fff21a0: 3fff23f3 000000f1 3fff23ec 4020fafb -3fff21b0: 3fff23f3 3fff21c0 3fff21d0 3fff23f6 -3fff21c0: 00000000 3fff23fb 4022321b 00000000 +3fff20d0: 202c3573 756f7247 2c302070 646e4920 +3fff20e0: 40236a6e 7954202c 45206570 00454358 +3fff20f0: 00000010 00000007 00000000 3fff2180 +3fff2100: 3fff2190 40107bfc 3fff3e4c 3fff22c0 +3fff2110: 40261934 000000f0 3fff22c0 401004d8 +3fff2120: 40238fcf 00000050 3fff2100 4021fc10 +3fff2130: 3fff32bc 4021680c 3ffeade1 4021ff7d +3fff2140: 3fff2190 3fff2180 0000000c 7fffffff +3fff2150: 00000019 00000000 00000000 3fff21c0 +3fff2160: 3fff23f3 3ffe8e08 00000000 4021ffb4 +3fff2170: 3fff2190 3fff2180 0000000c 40201118 +3fff2180: 3fff21c0 0000003c 3ffef840 00000007 +3fff2190: 00000000 00000000 00000000 40201128 +3fff21a0: 3fff23f3 000000f1 3fff23ec 4020fafb +3fff21b0: 3fff23f3 3fff21c0 3fff21d0 3fff23f6 +3fff21c0: 00000000 3fff23fb 4022321b 00000000 Exception 28: LoadProhibited: A load referenced a page mapped with an attribute that does not permit loads Decoding 14 results diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 5e6b55517..79dbf6f80 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -85,6 +85,7 @@ // -- HTTP ---------------------------------------- #define USE_WEBSERVER // Enable web server and wifi manager (+62k code, +4k mem) - Disable by // #define WEB_SERVER 2 // [WebServer] Web server (0 = Off, 1 = Start as User, 2 = Start as Admin) + #define WEB_PORT 80 // Web server Port for User and Admin mode #define WEB_USERNAME "admin" // Web server Admin mode user name #define WEB_PASSWORD "" // [WebPassword] Web server Admin mode Password for WEB_USERNAME (empty string = Disable) #define FRIENDLY_NAME "Sonoff" // [FriendlyName] Friendlyname up to 32 characters used by webpages and Alexa diff --git a/sonoff/webserver.ino b/sonoff/webserver.ino index de5a7d232..988f71447 100644 --- a/sonoff/webserver.ino +++ b/sonoff/webserver.ino @@ -278,7 +278,7 @@ void startWebserver(int type, IPAddress ipweb) if (!_httpflag) { if (!webServer) { - webServer = new ESP8266WebServer(80); + webServer = new ESP8266WebServer((type==HTTP_MANAGER)?80:WEB_PORT); webServer->on("/", handleRoot); webServer->on("/cn", handleConfig); webServer->on("/md", handleModule); @@ -805,7 +805,7 @@ void handleSave() { if (httpUser()) return; - char log[LOGSZ], stemp[20]; + char log[LOGSZ +20], stemp[20]; byte what = 0, restart; String result = ""; @@ -870,14 +870,16 @@ void handleSave() addLog(LOG_LEVEL_INFO, log); break; case 6: - sysCfg.module = (!strlen(webServer->arg("mt").c_str())) ? MODULE : atoi(webServer->arg("mt").c_str()); + byte new_module = (!strlen(webServer->arg("mt").c_str())) ? MODULE : atoi(webServer->arg("mt").c_str()); + byte new_modflg = (sysCfg.module != new_module); + sysCfg.module = new_module; mytmplt cmodule; memcpy_P(&cmodule, &modules[sysCfg.module], sizeof(cmodule)); String gpios = ""; for (byte i = 0; i < MAX_GPIO_PIN; i++) { if (cmodule.gp.io[i] == GPIO_USER) { snprintf_P(stemp, sizeof(stemp), PSTR("g%d"), i); - sysCfg.my_module.gp.io[i] = (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str()); + sysCfg.my_module.gp.io[i] = (new_modflg) ? 0 : (!strlen(webServer->arg(stemp).c_str())) ? 0 : atoi(webServer->arg(stemp).c_str()); gpios += F(", GPIO"); gpios += String(i); gpios += F(" "); gpios += String(sysCfg.my_module.gp.io[i]); } } @@ -961,7 +963,7 @@ void handleUpgrade() void handleUpgradeStart() { if (httpUser()) return; - char svalue[16]; // was MESSZ + char svalue[100]; // was MESSZ addLog_P(LOG_LEVEL_DEBUG, PSTR("HTTP: Firmware upgrade start")); WIFI_configCounter(); diff --git a/sonoff/xdrv_snfled.ino b/sonoff/xdrv_snfled.ino index d4df40ac4..b63c72ec2 100644 --- a/sonoff/xdrv_snfled.ino +++ b/sonoff/xdrv_snfled.ino @@ -27,9 +27,6 @@ POSSIBILITY OF SUCH DAMAGE. * Sonoff Led \*********************************************************************************************/ -#define ANALOG_WRITE_RANGE 255 // 127..1023 but as Color is addressed by 8 bits it should be 255 for my code -#define ANALOG_WRITE_FREQ 432 // 100..1000 Hz led refresh - uint8_t ledTable[] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -90,8 +87,8 @@ void sl_setDim(uint8_t myDimmer) void sl_init(void) { - analogWriteRange(ANALOG_WRITE_RANGE); // Default is 1023 (Arduino.h) - analogWriteFreq(ANALOG_WRITE_FREQ); // Default is 1000 (core_esp8266_wiring_pwm.c) - Try to lower flicker + sysCfg.pwmvalue[0] = 0; // We use led_color + sysCfg.pwmvalue[1] = 0; // We use led_color sl_blankv = 0; sl_power = 0; sl_any = 0; @@ -104,11 +101,14 @@ void sl_blank(byte state) // state = 0: No blank // 1: Blank led to solve flicker +/* + * Disabled when used with latest arduino-esp8266 pwm files if (sysCfg.module == SONOFF_LED) { sl_blankv = state; sl_wakeupActive = 0; sl_animate(); } +*/ } void sl_setPower(uint8_t power) @@ -172,8 +172,8 @@ void sl_animate() sl_lcolor[0] = sl_tcolor[0]; sl_lcolor[1] = sl_tcolor[1]; for (byte i = 0; i < 2; i++) { - if (pin[GPIO_PWM0 +i] < 99) { - analogWrite(pin[GPIO_PWM0 +i], (sysCfg.led_table) ? ledTable[sl_lcolor[i]] : sl_lcolor[i]); + if (pin[GPIO_PWM1 +i] < 99) { + analogWrite(pin[GPIO_PWM1 +i], ((sysCfg.led_table) ? ledTable[sl_lcolor[i]] : sl_lcolor[i]) * (PWM_RANGE / 255)); } } } diff --git a/sonoff/xsns_hlw8012.ino b/sonoff/xsns_hlw8012.ino index 5deaada5f..980aa0ada 100644 --- a/sonoff/xsns_hlw8012.ino +++ b/sonoff/xsns_hlw8012.ino @@ -438,19 +438,19 @@ boolean hlw_command(char *type, uint16_t index, char *dataBuf, uint16_t data_len } else if (!strcmp(type,"HLWPCAL")) { if ((data_len > 0) && (payload > 0) && (payload < 32001)) { - sysCfg.hlw_pcal = (payload == 1) ? HLW_PREF_PULSE : payload; + sysCfg.hlw_pcal = (payload > 9999) ? payload : HLW_PREF_PULSE; // 12530 } snprintf_P(svalue, ssvalue, PSTR("(\"HlwPcal\":\"%d%s\"}"), sysCfg.hlw_pcal, (sysCfg.value_units) ? " uS" : ""); } else if (!strcmp(type,"HLWUCAL")) { if ((data_len > 0) && (payload > 0) && (payload < 32001)) { - sysCfg.hlw_ucal = (payload == 1) ? HLW_UREF_PULSE : payload; + sysCfg.hlw_ucal = (payload > 999) ? payload : HLW_UREF_PULSE; // 1950 } snprintf_P(svalue, ssvalue, PSTR("{\"HlwUcal\":\"%d%s\"}"), sysCfg.hlw_ucal, (sysCfg.value_units) ? " uS" : ""); } else if (!strcmp(type,"HLWICAL")) { if ((data_len > 0) && (payload > 0) && (payload < 32001)) { - sysCfg.hlw_ical = (payload == 1) ? HLW_IREF_PULSE : payload; + sysCfg.hlw_ical = (payload > 2499) ? payload : HLW_IREF_PULSE; // 3500 } snprintf_P(svalue, ssvalue, PSTR("{\"HlwIcal\":\"%d%s\"}"), sysCfg.hlw_ical, (sysCfg.value_units) ? " uS" : ""); }