diff --git a/README.md b/README.md index 133fdbc36..911877430 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.1.2** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/_releasenotes.ino) for change information. +Current version is **4.1.3** - 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```. @@ -23,6 +23,7 @@ The following devices are supported: - [iTead S20 Smart Socket](http://sonoff.itead.cc/en/products/residential/s20-socket) - [iTead Slampher](http://sonoff.itead.cc/en/products/residential/slampher-rf) - [iTead Sonoff Touch](http://sonoff.itead.cc/en/products/residential/sonoff-touch) +- [iTead Sonoff SC](http://sonoff.itead.cc/en/products/residential/sonoff-sc) - [iTead Sonoff Led](http://sonoff.itead.cc/en/products/appliances/sonoff-led) - [iTead Sonoff Dev](https://www.itead.cc/sonoff-dev.html) - [iTead 1 Channel Switch 5V / 12V](https://www.itead.cc/smart-home/inching-self-locking-wifi-wireless-switch.html) diff --git a/api/arduino/sonoff.ino.bin b/api/arduino/sonoff.ino.bin index 25d0697b6..76edf11fa 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 7458ae9cd..fb81e060c 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,4 +1,12 @@ -/* 4.1.2 20170403 +/* 4.1.3 20170410 + * Add user configuarble GPIO to module S20 Socket and Slampher + * Add support for Sonoff SC (#112) + * Set PWM frequency from 1000Hz to 910Hz as used on iTead Sonoff Led firmware (#122) + * Set Sonoff Led unconfigured floating outputs to 0 to reduce exceptions due to power supply instabilities (#122) + * Add Access Point Mac Address to Status 11 and Telemetry (#329) + * Fix DS18B20 negative temperature readings (#334) + * + * 4.1.2 20170403 * Rename Unrecognised command to Unknown command * Remove all command lists * Remove command SmartConfig (superseded by WifiConfig) diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 2261f40f4..b73a4069c 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -10,7 +10,7 @@ * ==================================================== */ -#define VERSION 0x04010200 // 4.1.2 +#define VERSION 0x04010300 // 4.1.3 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}; @@ -104,7 +104,8 @@ enum emul_t {EMUL_NONE, EMUL_WEMO, EMUL_HUE, EMUL_MAX}; #define WS2812_MAX_LEDS 256 // Max number of LEDs #define PWM_RANGE 1023 // 255..1023 needs to be devisible by 256 -#define PWM_FREQ 1000 // 100..1000 Hz led refresh +//#define PWM_FREQ 1000 // 100..1000 Hz led refresh +#define PWM_FREQ 910 // 100..1000 Hz led refresh (iTead value) #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) @@ -1505,8 +1506,8 @@ void state_mqttPresent(char* svalue, uint16_t ssvalue) } snprintf_P(svalue, ssvalue, PSTR("%s\"%s\""), svalue, getStateText(bitRead(power, i))); } - snprintf_P(svalue, ssvalue, PSTR("%s, \"Wifi\":{\"AP\":%d, \"SSID\":\"%s\", \"RSSI\":%d}}"), - svalue, sysCfg.sta_active +1, sysCfg.sta_ssid[sysCfg.sta_active], WIFI_getRSSIasQuality(WiFi.RSSI())); + snprintf_P(svalue, ssvalue, PSTR("%s, \"Wifi\":{\"AP\":%d, \"SSID\":\"%s\", \"RSSI\":%d, \"APMac\":\"%s\"}}"), + svalue, sysCfg.sta_active +1, sysCfg.sta_ssid[sysCfg.sta_active], WIFI_getRSSIasQuality(WiFi.RSSI()), WiFi.BSSIDstr().c_str()); } void sensors_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson) @@ -1524,7 +1525,8 @@ void sensors_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson) snprintf_P(svalue, ssvalue, PSTR("%s, \"AnalogInput0\":%d"), svalue, analogRead(A0)); *djson = 1; } -#endif +#endif + if (sysCfg.module == SONOFF_SC) sc_mqttPresent(svalue, ssvalue, djson); if (pin[GPIO_DSB] < 99) { #ifdef USE_DS18B20 dsb_mqttPresent(svalue, ssvalue, djson); @@ -1915,6 +1917,8 @@ void stateloop() } } +/********************************************************************************************/ + void serial() { char log[LOGSZ]; @@ -1951,7 +1955,15 @@ void serial() SerialInByteCounter = 0; } } - if (SerialInByte == '\n') { + + if (SerialInByte == '\x1B') { // Sonoff SC status from ATMEGA328P + serialInBuf[SerialInByteCounter] = 0; // serial data completed + sc_rcvstat(serialInBuf); + SerialInByteCounter = 0; + Serial.flush(); + return; + } + else if (SerialInByte == '\n') { serialInBuf[SerialInByteCounter] = 0; // serial data completed seriallog_level = (sysCfg.seriallog_level < LOG_LEVEL_INFO) ? LOG_LEVEL_INFO : sysCfg.seriallog_level; snprintf_P(log, sizeof(log), PSTR("CMND: %s"), serialInBuf); @@ -2024,9 +2036,25 @@ void GPIO_init() Maxdevice = 4; Baudrate = 19200; } + else if (sysCfg.module == SONOFF_SC) { + Maxdevice = 0; + 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 + if (!my_module.gp.io[4]) { + pinMode(4, OUTPUT); // Stop floating outputs + digitalWrite(4, LOW); + } + if (!my_module.gp.io[5]) { + pinMode(5, OUTPUT); // Stop floating outputs + digitalWrite(5, LOW); + } + if (!my_module.gp.io[14]) { + pinMode(14, OUTPUT); // Stop floating outputs + digitalWrite(14, LOW); + } sl_init(); } else { @@ -2128,7 +2156,7 @@ void setup() if (Serial.baudRate() != Baudrate) { if (seriallog_level) { - snprintf_P(log, sizeof(log), PSTR("APP: Change your baudrate to %d"), Baudrate); + snprintf_P(log, sizeof(log), PSTR("APP: Set baudrate to %d"), Baudrate); addLog(LOG_LEVEL_INFO, log); } delay(100); @@ -2172,6 +2200,8 @@ void setup() } blink_powersave = power; + if (sysCfg.module == SONOFF_SC) sc_init(); + rtc_init(); snprintf_P(log, sizeof(log), PSTR("APP: Project %s %s (Topic %s, Fallback %s, GroupTopic %s) Version %s"), diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 3b6a3b634..142e2f759 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -121,6 +121,7 @@ enum module_t { WEMOS, SONOFF_DEV, H801, + SONOFF_SC, MAXMODULE }; /********************************************************************************************/ @@ -240,7 +241,10 @@ const mytmplt modules[MAXMODULE] PROGMEM = { }, { "S20 Socket", // S20 Smart Socket (ESP8266) 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, 0, 0, 0, // Flash connection GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) @@ -248,7 +252,10 @@ const mytmplt modules[MAXMODULE] PROGMEM = { }, { "Slampher", // Slampher (ESP8266) 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, 0, 0, 0, // Flash connection GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) @@ -391,6 +398,17 @@ const mytmplt modules[MAXMODULE] PROGMEM = { GPIO_PWM1, // GPIO14 W1 GPIO_PWM5, // GPIO15 Red 0, 0 + }, + { "Sonoff SC", // Sonoff SC (ESP8266) + GPIO_KEY1, // GPIO00 Button + GPIO_TXD, // GPIO01 RXD to ATMEGA328P + GPIO_USER, // GPIO02 Optional sensor + GPIO_RXD, // GPIO03 TXD to ATMEGA328P + 0, 0, + 0, 0, 0, 0, 0, 0, // Flash connection + 0, + GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) + 0, 0, 0, 0 } }; diff --git a/sonoff/webserver.ino b/sonoff/webserver.ino index f131bd934..fcc832b47 100644 --- a/sonoff/webserver.ino +++ b/sonoff/webserver.ino @@ -267,6 +267,12 @@ const char HTTP_SNS_HUM[] PROGMEM = "%s Humidity%s%"; const char HTTP_SNS_PRESSURE[] PROGMEM = "%s Pressure%s hPa"; +const char HTTP_SNS_LIGHT[] PROGMEM = + "%s Light%d of 10"; +const char HTTP_SNS_NOISE[] PROGMEM = + "%s Noise%d of 10"; +const char HTTP_SNS_DUST[] PROGMEM = + "%s Air quality%d of 10"; const char HTTP_END[] PROGMEM = "" "" @@ -452,6 +458,7 @@ void handleAjax2() String tpage = ""; if (hlw_flg) tpage += hlw_webPresent(); + if (sysCfg.module == SONOFF_SC) tpage += sc_webPresent(); #ifdef USE_DS18B20 if (pin[GPIO_DSB] < 99) tpage += dsb_webPresent(); #endif // USE_DS18B20 diff --git a/sonoff/xdrv_snfsc.ino b/sonoff/xdrv_snfsc.ino new file mode 100644 index 000000000..e3f387eea --- /dev/null +++ b/sonoff/xdrv_snfsc.ino @@ -0,0 +1,149 @@ +/* +Copyright (c) 2017 Theo Arends. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +/*********************************************************************************************\ + * Sonoff Sc + * + * sc_value[0] DHT11 Humidity + * sc_value[1] DHT11 Temperature in Celsius + * sc_value[2] Light level from 1 (Dark) to 10 (Bright) - inverted from original + * sc_value[3] Noise level from 1 (Quiet) to 10 (Loud) + * sc_value[4] Air Quality level from 1 (Bad) to 10 (Good) - inverted from original + * + * To ATMEGA328P: + * AT+DEVCONFIG="uploadFreq":1800,"humiThreshold":2,"tempThreshold":1[1B] + * AT+NOTIFY="uploadFreq":1800,"humiThreshold":2,"tempThreshold":1[1B] + * response: AT+NOTIFY=ok[1B] + * AT+SEND=fail[1B] + * AT+SEND=ok[1B] + * AT+STATUS=4[1B] + * AT+STATUS[1B] + * AT+START[1B] + * + * From ATMEGA328P: + * AT+UPDATE="humidity":42,"temperature":20,"light":7,"noise":3,"dusty":1[1B] + * response: AT+SEND=ok[1B] or AT+SEND=fail[1B] + * AT+STATUS?[1B] + * response: AT+STATUS=4[1B] + * + * Sequence: + * SC sends: ATMEGA328P sends: + * AT+START[1B] + * AT+UPDATE="humidity":42,"temperature":20,"light":7,"noise":3,"dusty":1[1B] + * AT+SEND=ok[1B] + * + * AT+STATUS?[1B] + * AT+STATUS=4[1B] + * +\*********************************************************************************************/ + +uint16_t sc_value[5] = { 0 }; + +void sc_init() +{ +// Serial.write("AT+DEVCONFIG=\"uploadFreq\":1800\e"); + Serial.write("AT+START\e"); +// Serial.write("AT+STATUS\e"); +} + +void sc_rcvstat(char *rcvstat) +{ + char *p, *str; + uint16_t value[5] = { 0 }; + + if (!strncmp(rcvstat,"AT+UPDATE=",10)) { + int8_t i = -1; + for (str = strtok_r(rcvstat, ":", &p); str && i < 5; str = strtok_r(NULL, ":", &p)) value[i++] = atoi(str); + if (value[0] > 0) { + for (byte i = 0; i < 5; i++) sc_value[i] = value[i]; + sc_value[2] = 11 - sc_value[2]; // Invert light level + sc_value[4] = 11 - sc_value[4]; // Invert dust level + Serial.write("AT+SEND=ok\e"); + } else { + Serial.write("AT+SEND=fail\e"); + } + } + else if (!strcmp(rcvstat,"AT+STATUS?")) { + Serial.write("AT+STATUS=4\e"); + } +} + +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +float sc_convertCtoF(float c) +{ + return c * 1.8 + 32; +} + +void sc_mqttPresent(char* svalue, uint16_t ssvalue, uint8_t* djson) +{ + if (sc_value[0] > 0) { + char stemp1[10], stemp2[10]; + + float t = sc_value[1]; + if (TEMP_CONVERSION) t = sc_convertCtoF(t); + dtostrf(t, 1, TEMP_RESOLUTION &3, stemp1); + float h = sc_value[0]; + dtostrf(h, 1, HUMIDITY_RESOLUTION &3, stemp2); + snprintf_P(svalue, ssvalue, PSTR("%s, \"SC\":{\"Temperature\":%s, \"Humidity\":%s, \"Light\":%d, \"Noise\":%d, \"AirQuality\":%d}"), + svalue, stemp1, stemp2, sc_value[2], sc_value[3], sc_value[4]); + *djson = 1; +#ifdef USE_DOMOTICZ + domoticz_sensor2(stemp1, stemp2); + domoticz_sensor5(sc_value[2]); +#endif // USE_DOMOTICZ + } +} + +#ifdef USE_WEBSERVER +String sc_webPresent() +{ + String page = ""; + + if (sc_value[0] > 0) { + char stemp[10], sensor[80], scstype[] = "SC"; + + float t = sc_value[1]; + if (TEMP_CONVERSION) t = sc_convertCtoF(t); + dtostrf(t, 1, TEMP_RESOLUTION &3, stemp); + snprintf_P(sensor, sizeof(sensor), HTTP_SNS_TEMP, scstype, stemp, (TEMP_CONVERSION) ? 'F' : 'C'); + page += sensor; + float h = sc_value[0]; + dtostrf(h, 1, HUMIDITY_RESOLUTION &3, stemp); + snprintf_P(sensor, sizeof(sensor), HTTP_SNS_HUM, scstype, stemp); + page += sensor; + snprintf_P(sensor, sizeof(sensor), HTTP_SNS_LIGHT, scstype, sc_value[2]); + page += sensor; + snprintf_P(sensor, sizeof(sensor), HTTP_SNS_NOISE, scstype, sc_value[3]); + page += sensor; + snprintf_P(sensor, sizeof(sensor), HTTP_SNS_DUST, scstype, sc_value[4]); + page += sensor; + } + return page; +} +#endif // USE_WEBSERVER + diff --git a/sonoff/xsns_ds18b20.ino b/sonoff/xsns_ds18b20.ino index 54341d2e5..3735d9a36 100644 --- a/sonoff/xsns_ds18b20.ino +++ b/sonoff/xsns_ds18b20.ino @@ -131,7 +131,7 @@ float dsb_convertCtoF(float c) boolean dsb_readTemp(bool S, float &t) { int16_t DSTemp; - byte msb, lsb, crc; + byte msb, lsb, crc, sign = 1; if (!dsb_mt) { t = NAN; @@ -168,7 +168,11 @@ boolean dsb_readTemp(bool S, float &t) addLog_P(LOG_LEVEL_DEBUG, PSTR("DSB: Sensor CRC error")); } else { DSTemp = (msb << 8) + lsb; - t = (float(DSTemp) * 0.0625); + if (DSTemp > 2047) { + DSTemp = (~DSTemp) +1; + sign = -1; + } + t = (float)sign * DSTemp * 0.0625; if(S) t = dsb_convertCtoF(t); } if (!isnan(t)) dsb_mt = t; diff --git a/sonoff/xsns_ds18x20.ino b/sonoff/xsns_ds18x20.ino index 81c48ec63..6fc3fec89 100644 --- a/sonoff/xsns_ds18x20.ino +++ b/sonoff/xsns_ds18x20.ino @@ -154,7 +154,12 @@ boolean ds18x20_read(uint8_t sensor, bool S, float &t) break; case DS18B20_CHIPID: // DS18B20 case MAX31850_CHIPID: // MAX31850 - t = ((data[1] << 8) + data[0]) * 0.0625; + uint16_t temp12 = (data[1] << 8) + data[0]; + if (temp12 > 2047) { + temp12 = (~temp12) +1; + sign = -1; + } + t = sign * temp12 * 0.0625; if(S) t = ds18x20_convertCtoF(t); break; }