diff --git a/README.md b/README.md index 22116ef91..fa7230d1e 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ There is **NO CONFLICT** with MQTT, Home Assistant, Web, etc. Tests show fast re ## 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.12.0g** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for change information. +Current version is **5.12.0h** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for change information. ### ATTENTION All versions diff --git a/lib/TasmotaSerial-1.1.0/README.md b/lib/TasmotaSerial-1.2.0/README.md similarity index 79% rename from lib/TasmotaSerial-1.1.0/README.md rename to lib/TasmotaSerial-1.2.0/README.md index 5c7a24721..205a9597c 100644 --- a/lib/TasmotaSerial-1.1.0/README.md +++ b/lib/TasmotaSerial-1.2.0/README.md @@ -1,6 +1,6 @@ # TasmotaSerial -Implementation of software serial library for the ESP8266 at 9600 baud +Implementation of software serial library for the ESP8266 Allows for several instances to be active at the same time. diff --git a/lib/TasmotaSerial-1.1.0/examples/swsertest/swsertest.ino b/lib/TasmotaSerial-1.2.0/examples/swsertest/swsertest.ino similarity index 100% rename from lib/TasmotaSerial-1.1.0/examples/swsertest/swsertest.ino rename to lib/TasmotaSerial-1.2.0/examples/swsertest/swsertest.ino diff --git a/lib/TasmotaSerial-1.1.0/keywords.txt b/lib/TasmotaSerial-1.2.0/keywords.txt similarity index 100% rename from lib/TasmotaSerial-1.1.0/keywords.txt rename to lib/TasmotaSerial-1.2.0/keywords.txt diff --git a/lib/TasmotaSerial-1.1.0/library.json b/lib/TasmotaSerial-1.2.0/library.json similarity index 89% rename from lib/TasmotaSerial-1.1.0/library.json rename to lib/TasmotaSerial-1.2.0/library.json index 986c6d7ac..e4b2ba8a3 100644 --- a/lib/TasmotaSerial-1.1.0/library.json +++ b/lib/TasmotaSerial-1.2.0/library.json @@ -1,10 +1,10 @@ { "name": "TasmotaSerial", - "version": "1.0.0", + "version": "1.2.0", "keywords": [ "serial", "io", "TasmotaSerial" ], - "description": "Implementation of software serial for ESP8266 at 9600 baud.", + "description": "Implementation of software serial for ESP8266.", "repository": { "type": "git", diff --git a/lib/TasmotaSerial-1.1.0/library.properties b/lib/TasmotaSerial-1.2.0/library.properties similarity index 64% rename from lib/TasmotaSerial-1.1.0/library.properties rename to lib/TasmotaSerial-1.2.0/library.properties index 703b613ee..d427d0fb3 100644 --- a/lib/TasmotaSerial-1.1.0/library.properties +++ b/lib/TasmotaSerial-1.2.0/library.properties @@ -1,8 +1,8 @@ name=TasmotaSerial -version=1.0 +version=1.2.0 author=Theo Arends maintainer=Theo Arends -sentence=Implementation of software serial for ESP8266 at 9600 baud. +sentence=Implementation of software serial for ESP8266. paragraph= category=Signal Input/Output url= diff --git a/lib/TasmotaSerial-1.1.0/src/TasmotaSerial.cpp b/lib/TasmotaSerial-1.2.0/src/TasmotaSerial.cpp similarity index 91% rename from lib/TasmotaSerial-1.1.0/src/TasmotaSerial.cpp rename to lib/TasmotaSerial-1.2.0/src/TasmotaSerial.cpp index 35881dfb3..c189442e6 100644 --- a/lib/TasmotaSerial-1.1.0/src/TasmotaSerial.cpp +++ b/lib/TasmotaSerial-1.2.0/src/TasmotaSerial.cpp @@ -87,9 +87,7 @@ TasmotaSerial::TasmotaSerial(int receive_pin, int transmit_pin) m_in_pos = m_out_pos = 0; if (m_rx_pin > -1) { m_buffer = (uint8_t*)malloc(TM_SERIAL_BUFFER_SIZE); - if (m_buffer == NULL) { - return; - } + if (m_buffer == NULL) return; // Use getCycleCount() loop to get as exact timing as possible m_bit_time = ESP.getCpuFreqMHz() *1000000 /TM_SERIAL_BAUDRATE; pinMode(m_rx_pin, INPUT); @@ -111,7 +109,8 @@ bool TasmotaSerial::isValidGPIOpin(int pin) bool TasmotaSerial::begin(long speed) { // Use getCycleCount() loop to get as exact timing as possible m_bit_time = ESP.getCpuFreqMHz() *1000000 /speed; - return m_valid && (speed <= TM_SERIAL_BAUDRATE); + m_high_speed = (speed > 9600); + return m_valid; } bool TasmotaSerial::begin() { @@ -123,17 +122,13 @@ void TasmotaSerial::flush() { } int TasmotaSerial::peek() { - if ((-1 == m_rx_pin) || (m_in_pos == m_out_pos)) { - return -1; - } + if ((-1 == m_rx_pin) || (m_in_pos == m_out_pos)) return -1; return m_buffer[m_out_pos]; } int TasmotaSerial::read() { - if ((-1 == m_rx_pin) || (m_in_pos == m_out_pos)) { - return -1; - } + if ((-1 == m_rx_pin) || (m_in_pos == m_out_pos)) return -1; uint8_t ch = m_buffer[m_out_pos]; m_out_pos = (m_out_pos +1) % TM_SERIAL_BUFFER_SIZE; return ch; @@ -142,23 +137,20 @@ int TasmotaSerial::read() int TasmotaSerial::available() { int avail = m_in_pos - m_out_pos; - if (avail < 0) { - avail += TM_SERIAL_BUFFER_SIZE; - } + if (avail < 0) avail += TM_SERIAL_BUFFER_SIZE; return avail; } #ifdef TM_SERIAL_USE_IRAM -#define TM_SERIAL_WAIT { while (ESP.getCycleCount()-start < wait) optimistic_yield(1); wait += m_bit_time; } // Watchdog timeouts +#define TM_SERIAL_WAIT { while (ESP.getCycleCount()-start < wait) if (!m_high_speed) optimistic_yield(1); wait += m_bit_time; } // Watchdog timeouts #else #define TM_SERIAL_WAIT { while (ESP.getCycleCount()-start < wait); wait += m_bit_time; } #endif size_t TasmotaSerial::write(uint8_t b) { - if (-1 == m_tx_pin) { - return 0; - } + if (-1 == m_tx_pin) return 0; + if (m_high_speed) cli(); // Disable interrupts in order to get a clean transmit unsigned long wait = m_bit_time; digitalWrite(m_tx_pin, HIGH); unsigned long start = ESP.getCycleCount(); @@ -173,6 +165,7 @@ size_t TasmotaSerial::write(uint8_t b) // Stop bit digitalWrite(m_tx_pin, HIGH); TM_SERIAL_WAIT; + if (m_high_speed) sei(); return 1; } @@ -191,9 +184,7 @@ void TasmotaSerial::rxRead() for (int i = 0; i < 8; i++) { TM_SERIAL_WAIT; rec >>= 1; - if (digitalRead(m_rx_pin)) { - rec |= 0x80; - } + if (digitalRead(m_rx_pin)) rec |= 0x80; } // Stop bit TM_SERIAL_WAIT; diff --git a/lib/TasmotaSerial-1.1.0/src/TasmotaSerial.h b/lib/TasmotaSerial-1.2.0/src/TasmotaSerial.h similarity index 91% rename from lib/TasmotaSerial-1.1.0/src/TasmotaSerial.h rename to lib/TasmotaSerial-1.2.0/src/TasmotaSerial.h index d18dd335f..0876590fe 100644 --- a/lib/TasmotaSerial-1.1.0/src/TasmotaSerial.h +++ b/lib/TasmotaSerial-1.2.0/src/TasmotaSerial.h @@ -20,12 +20,12 @@ #ifndef TasmotaSerial_h #define TasmotaSerial_h /*********************************************************************************************\ - * TasmotaSerial supports up to 9600 baud with fixed buffer size of 64 bytes using optional no iram + * TasmotaSerial supports up to 115200 baud with fixed buffer size of 64 bytes using optional no iram * * Based on EspSoftwareSerial v3.3.1 by Peter Lerup (https://github.com/plerup/espsoftwareserial) \*********************************************************************************************/ -#define TM_SERIAL_BAUDRATE 9600 // Max supported baudrate +#define TM_SERIAL_BAUDRATE 9600 // Default baudrate #define TM_SERIAL_BUFFER_SIZE 64 // Receive buffer size #include // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0) @@ -57,6 +57,7 @@ class TasmotaSerial : public Stream { // Member variables bool m_valid; + bool m_high_speed; int m_rx_pin; int m_tx_pin; unsigned long m_bit_time; diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index ca3d6c7a7..dc40e3a5b 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,5 +1,15 @@ -/* 5.12.0g - * Add support for MQTT to hardware serial bridge using commands Baudrate and SerialSend. Currently supports 8N1 and text only (#2182) +/* 5.12.0h + * Add optional Arduino OTA support to be enabled in user_config.h (#1998) + * Add support for Software Serial bridge using commands SerialDelimiter, SBaudrate and SSerialSend. Supports 8N1 and text only (#2190) + * Add support for Hardware Serial bridge using commands SerialDelimiter, Baudrate and SerialSend. Supports 8N1 and text only (#2182) + * Add support for Zengge WF017 PWM Led strip controller (#2202) + * Add PWM status to command State if PWM enabled (#2203) + * Add command HSBColor Hue,Sat,Bri (#1642, #2203) + * Add command Channel 0..100 to control dimmer value for individual color channels (#2111, #2203) + * Add Channel status information (#2211) + * Add all FriendlyNames to Status information (#2208) + * Change status display of Ssid and SetOption + * Change default option SetOption15 from 0 to 1 providing better initial PWM experience * * 5.12.0f * Add compile time support for WS2812 BRG and RBG led configurations to be defined in user_config.h (#1690) diff --git a/sonoff/i18n.h b/sonoff/i18n.h index d59143209..71a795297 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -63,6 +63,7 @@ #define D_JSON_GATEWAY "Gateway" #define D_JSON_HEAPSIZE "Heap" #define D_JSON_HIGH "High" +#define D_JSON_HSBCOLOR "HSBColor" #define D_JSON_HUMIDITY "Humidity" #define D_JSON_I2CSCAN_DEVICES_FOUND_AT "Device(s) found at" #define D_JSON_I2CSCAN_UNKNOWN_ERROR_AT "Unknown error at" @@ -242,13 +243,16 @@ #define D_CMND_CFGDUMP "CfgDump" #define D_CMND_I2CSCAN "I2CScan" #define D_CMND_SERIALSEND "SerialSend" +#define D_CMND_SERIALDELIMITER "SerialDelimiter" #define D_CMND_BAUDRATE "Baudrate" #define D_CMND_EXCEPTION "Exception" // Commands xdrv_01_light.ino +#define D_CMND_CHANNEL "Channel" #define D_CMND_COLOR "Color" #define D_CMND_COLORTEMPERATURE "CT" #define D_CMND_DIMMER "Dimmer" +#define D_CMND_HSBCOLOR "HSBColor" #define D_CMND_LED "Led" #define D_CMND_LEDTABLE "LedTable" #define D_CMND_FADE "Fade" @@ -340,6 +344,11 @@ #define D_CMND_DISP_SIZE "Size" #define D_CMND_DISP_TEXT "Text" +// Commands xdrv_08_serial_bridge.ino +#define D_CMND_SSERIALSEND "SSerialSend" +#define D_CMND_SBAUDRATE "SBaudrate" + #define D_JSON_SSERIALRECEIVED "SSerialReceived" + /********************************************************************************************/ #ifndef MY_LANGUAGE @@ -477,4 +486,4 @@ const char S_INFORMATION[] PROGMEM = D_INFORMATION; const char S_RESTART[] PROGMEM = D_RESTART; #endif // USE_WEBSERVER -#endif // _I18N_H_ \ No newline at end of file +#endif // _I18N_H_ diff --git a/sonoff/language/cs-CZ.h b/sonoff/language/cs-CZ.h index 5f60ae8c1..e78d0a8e0 100644 --- a/sonoff/language/cs-CZ.h +++ b/sonoff/language/cs-CZ.h @@ -412,6 +412,8 @@ #define D_SENSOR_BACKLIGHT "BkLight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1 "SDS0X1" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index 4ba502af3..b0ea8033a 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -412,6 +412,8 @@ #define D_SENSOR_BACKLIGHT "BkLight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1 "SDS0X1" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index 996b859e1..6191d391d 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -427,6 +427,8 @@ #define D_SENSOR_BACKLIGHT "BkLight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1 "SDS0X1" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/es-AR.h b/sonoff/language/es-AR.h index cbca9943b..6799b02d0 100644 --- a/sonoff/language/es-AR.h +++ b/sonoff/language/es-AR.h @@ -427,6 +427,8 @@ #define D_SENSOR_BACKLIGHT "BkLight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1 "SDS0X1" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index 4690bab6e..7b9c0137f 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -412,6 +412,8 @@ #define D_SENSOR_BACKLIGHT "BkLight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1 "SDS0X1" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/hu-HU.h b/sonoff/language/hu-HU.h index 613008ae0..82dde4c38 100644 --- a/sonoff/language/hu-HU.h +++ b/sonoff/language/hu-HU.h @@ -412,6 +412,8 @@ #define D_SENSOR_BACKLIGHT "Háttérvil" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1 "SDS0X1" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index 67752fd81..4e16869a4 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -412,6 +412,8 @@ #define D_SENSOR_BACKLIGHT "BkLight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1 "SDS0X1" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index 3a02ded56..53bdb7f67 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -412,6 +412,8 @@ #define D_SENSOR_BACKLIGHT "BkLight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1 "SDS0X1" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index 6acc8a7f6..90a69afb5 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -412,6 +412,8 @@ #define D_SENSOR_BACKLIGHT "BkLight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1 "SDS0X1" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h index 180d316d7..7d8f0512c 100644 --- a/sonoff/language/pt-PT.h +++ b/sonoff/language/pt-PT.h @@ -412,6 +412,8 @@ #define D_SENSOR_BACKLIGHT "Luz negra" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1 "SDS0X1" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h index d2626ae5f..640b8bef7 100644 --- a/sonoff/language/ru-RU.h +++ b/sonoff/language/ru-RU.h @@ -412,6 +412,8 @@ #define D_SENSOR_BACKLIGHT "BkLight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1 "SDS0X1" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" // Units #define D_UNIT_AMPERE "А" diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h index eb0351508..0cd86bcc8 100644 --- a/sonoff/language/zh-CN.h +++ b/sonoff/language/zh-CN.h @@ -412,6 +412,8 @@ #define D_SENSOR_BACKLIGHT "BkLight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1 "SDS0X1" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" // Units #define D_UNIT_AMPERE "安" diff --git a/sonoff/language/zh-TW.h b/sonoff/language/zh-TW.h index cab33a14b..c3efc118b 100644 --- a/sonoff/language/zh-TW.h +++ b/sonoff/language/zh-TW.h @@ -412,6 +412,8 @@ #define D_SENSOR_BACKLIGHT "BkLight" #define D_SENSOR_PMS5003 "PMS5003" #define D_SENSOR_SDS0X1 "SDS0X1" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" // Units #define D_UNIT_AMPERE "安" diff --git a/sonoff/settings.h b/sonoff/settings.h index 3c3caf9da..936e54963 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -192,9 +192,8 @@ struct SYSCFG { uint16_t ws_wakeup; // 3AA Not used since 5.8.0 char friendlyname[MAX_FRIENDLYNAMES][33]; // 3AC char switch_topic[33]; // 430 - - byte free_451[2]; // 451 - + char serial_delimiter; // 451 + uint8_t sbaudrate; // 452 uint8_t sleep; // 453 uint16_t domoticz_switch_idx[MAX_DOMOTICZ_IDX]; // 454 uint16_t domoticz_sensor_idx[MAX_DOMOTICZ_SNS_IDX]; // 45C diff --git a/sonoff/settings.ino b/sonoff/settings.ino index 84f68ba08..ac706f4aa 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -450,6 +450,7 @@ void SettingsDefaultSet2() Settings.flag.mqtt_power_retain = MQTT_POWER_RETAIN; Settings.flag.mqtt_button_retain = MQTT_BUTTON_RETAIN; Settings.flag.mqtt_switch_retain = MQTT_SWITCH_RETAIN; + Settings.flag.pwm_control = 1; Settings.flag.hass_discovery = HOME_ASSISTANT_DISCOVERY_ENABLE; Settings.flag2.emulation = EMULATION; @@ -458,6 +459,8 @@ void SettingsDefaultSet2() Settings.timezone = APP_TIMEZONE; strlcpy(Settings.ota_url, OTA_URL, sizeof(Settings.ota_url)); Settings.baudrate = APP_BAUDRATE / 1200; + Settings.sbaudrate = SOFT_BAUDRATE / 1200; + Settings.serial_delimiter = 0xff; Settings.seriallog_level = SERIAL_LOG_LEVEL; // Settings.sta_active = 0; @@ -523,14 +526,14 @@ void SettingsDefaultSet2() // Settings.energy_max_voltage = 0; // Settings.energy_min_current = 0; // Settings.energy_max_current = 0; -// Settings.energy_max_power_limit = 0; // MaxPowerLimit +// Settings.energy_max_power_limit = 0; // MaxPowerLimit Settings.energy_max_power_limit_hold = MAX_POWER_HOLD; Settings.energy_max_power_limit_window = MAX_POWER_WINDOW; -// Settings.energy_max_power_safe_limit = 0; // MaxSafePowerLimit +// Settings.energy_max_power_safe_limit = 0; // MaxSafePowerLimit Settings.energy_max_power_safe_limit_hold = SAFE_POWER_HOLD; Settings.energy_max_power_safe_limit_window = SAFE_POWER_WINDOW; -// Settings.energy_max_energy = 0; // MaxEnergy -// Settings.energy_max_energy_start = 0; // MaxEnergyStart +// Settings.energy_max_energy = 0; // MaxEnergy +// Settings.energy_max_energy_start = 0; // MaxEnergyStart SettingsDefaultSet_3_2_4(); @@ -903,6 +906,10 @@ void SettingsDelta() if (Settings.version < 0x050C0007) { Settings.baudrate = APP_BAUDRATE / 1200; } + if (Settings.version < 0x050C0008) { + Settings.sbaudrate = SOFT_BAUDRATE / 1200; + Settings.serial_delimiter = 0xff; + } Settings.version = VERSION; SettingsSave(1); diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index 0b90213bd..58079e2df 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -98,6 +98,7 @@ typedef unsigned long power_t; // Power (Relay) type #define MAX_BACKLOG 16 // Max number of commands in backlog (chk backlog_index and backlog_pointer code) #define MIN_BACKLOG_DELAY 2 // Minimal backlog delay in 0.1 seconds +#define SOFT_BAUDRATE 9600 // Default software serial baudrate #define APP_BAUDRATE 115200 // Default serial baudrate #define SERIAL_POLLING 100 // Serial receive polling in ms #define MAX_STATUS 11 // Max number of status lines diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 01110a253..ab4a51f1b 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -25,7 +25,7 @@ - Select IDE Tools - Flash Size: "1M (no SPIFFS)" ====================================================*/ -#define VERSION 0x050C0007 // 5.12.0g +#define VERSION 0x050C0008 // 5.12.0h // Location specific includes #include // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_3_0) @@ -54,8 +54,14 @@ #include // WifiManager, Webserver #include // WifiManager #endif // USE_WEBSERVER +#ifdef USE_ARDUINO_OTA + #include // Arduino OTA + #ifndef USE_DISCOVERY + #define USE_DISCOVERY + #endif +#endif // USE_ARDUINO_OTA #ifdef USE_DISCOVERY - #include // MQTT, Webserver + #include // MQTT, Webserver, Arduino OTA #endif // USE_DISCOVERY #ifdef USE_I2C #include // I2C support library @@ -76,7 +82,7 @@ enum TasmotaCommands { CMND_LOGHOST, CMND_LOGPORT, CMND_IPADDRESS, CMND_NTPSERVER, CMND_AP, CMND_SSID, CMND_PASSWORD, CMND_HOSTNAME, CMND_WIFICONFIG, CMND_FRIENDLYNAME, CMND_SWITCHMODE, CMND_WEBSERVER, CMND_WEBPASSWORD, CMND_WEBLOG, CMND_EMULATION, CMND_TELEPERIOD, CMND_RESTART, CMND_RESET, CMND_TIMEZONE, CMND_ALTITUDE, CMND_LEDPOWER, CMND_LEDSTATE, - CMND_CFGDUMP, CMND_I2CSCAN, CMND_SERIALSEND, CMND_BAUDRATE, CMND_EXCEPTION }; + CMND_CFGDUMP, CMND_I2CSCAN, CMND_SERIALSEND, CMND_BAUDRATE, CMND_SERIALDELIMITER, CMND_EXCEPTION }; const char kTasmotaCommands[] PROGMEM = D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|" D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SENSOR "|" D_CMND_SAVEDATA "|" D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|" @@ -86,7 +92,7 @@ const char kTasmotaCommands[] PROGMEM = D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_WEBSERVER "|" D_CMND_WEBPASSWORD "|" D_CMND_WEBLOG "|" D_CMND_EMULATION "|" D_CMND_TELEPERIOD "|" D_CMND_RESTART "|" D_CMND_RESET "|" D_CMND_TIMEZONE "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" - D_CMND_CFGDUMP "|" D_CMND_I2CSCAN "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE + D_CMND_CFGDUMP "|" D_CMND_I2CSCAN "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALDELIMITER #ifdef DEBUG_THEO "|" D_CMND_EXCEPTION #endif @@ -176,7 +182,6 @@ uint8_t i2c_flg = 0; // I2C configured uint8_t spi_flg = 0; // SPI configured uint8_t light_type = 0; // Light types bool pwm_present = false; // Any PWM channel configured with SetOption15 0 - boolean mdns_begun = false; char my_version[33]; // Composed version string @@ -737,15 +742,9 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) Settings.pwm_value[index -1] = payload; analogWrite(pin[GPIO_PWM1 + index -1], bitRead(pwm_inverted, index -1) ? Settings.pwm_range - payload : payload); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_PWM "\":{")); - bool first = true; - for (byte i = 0; i < MAX_PWMS; i++) { - if (pin[GPIO_PWM1 + i] < 99) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s\"" D_CMND_PWM "%d\":%d"), mqtt_data, first ? "" : ",", i+1, Settings.pwm_value[i]); - first = false; - } - } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}}"),mqtt_data); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{")); + MqttShowPWMState(); // Render the PWM status to MQTT + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); } else if (CMND_PWMFREQUENCY == command_code) { if ((1 == payload) || ((payload >= 100) && (payload <= 4000))) { @@ -821,15 +820,35 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.baudrate * 1200); } - else if ((CMND_SERIALSEND == command_code) && (index > 0) && (index <= 2)) { + else if ((CMND_SERIALSEND == command_code) && (index > 0) && (index <= 3)) { SetSeriallog(LOG_LEVEL_NONE); Settings.flag.mqtt_serial = 1; if (data_len > 0) { - if (1 == index) Serial.printf("%s\n", dataBuf); - if (2 == index) Serial.printf("%s", dataBuf); + if (1 == index) { + Serial.printf("%s\n", dataBuf); + } + else if (2 == index) { + Serial.printf("%s", dataBuf); + } + else if (3 == index) { + uint16_t dat_len = data_len; + Serial.printf("%s", Unescape(dataBuf, &dat_len)); + } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); } } + else if (CMND_SERIALDELIMITER == command_code) { + if ((data_len > 0) && (payload < 256)) { + if (payload > 0) { + Settings.serial_delimiter = payload; + } else { + uint16_t dat_len = data_len; + Unescape(dataBuf, &dat_len); + Settings.serial_delimiter = dataBuf[0]; + } + } + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.serial_delimiter); + } else if (CMND_SERIALLOG == command_code) { if ((payload >= LOG_LEVEL_NONE) && (payload <= LOG_LEVEL_ALL)) { Settings.flag.mqtt_serial = 0; @@ -1229,6 +1248,7 @@ void ExecuteCommand(char *cmnd) void PublishStatus(uint8_t payload) { uint8_t option = 1; + char stemp[MAX_FRIENDLYNAMES * (sizeof(Settings.friendlyname[0]) +4)]; // Workaround MQTT - TCP/IP stack queueing when SUB_PREFIX = PUB_PREFIX if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1]) && (!payload)) option++; @@ -1237,8 +1257,13 @@ void PublishStatus(uint8_t payload) if (!energy_flg && (9 == payload)) payload = 99; if ((0 == payload) || (99 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":\"%s\",\"" D_CMND_TOPIC "\":\"%s\",\"" D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"), - Settings.module +1, Settings.friendlyname[0], mqtt_topic, Settings.button_topic, power, Settings.poweronstate, Settings.ledstate, Settings.save_data, Settings.flag.save_state, Settings.flag.mqtt_button_retain, Settings.flag.mqtt_power_retain); + uint8_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present; + stemp[0] = '\0'; + for (byte i = 0; i < maxfn; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), Settings.friendlyname[i]); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\"" D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"), + Settings.module +1, stemp, mqtt_topic, Settings.button_topic, power, Settings.poweronstate, Settings.ledstate, Settings.save_data, Settings.flag.save_state, Settings.flag.mqtt_button_retain, Settings.flag.mqtt_power_retain); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS)); } @@ -1255,8 +1280,8 @@ void PublishStatus(uint8_t payload) } if ((0 == payload) || (3 == payload)) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_SYSLOG "\":%d,\"" D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\"" D_CMND_SSID "1\":\"%s\",\"" D_CMND_SSID "2\":\"%s\",\"" D_CMND_TELEPERIOD "\":%d,\"" D_CMND_SETOPTION "\":\"%08X\"}}"), - Settings.seriallog_level, Settings.weblog_level, Settings.syslog_level, Settings.syslog_host, Settings.syslog_port, Settings.sta_ssid[0], Settings.sta_ssid[1], Settings.tele_period, Settings.flag.data); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_SYSLOG "\":%d,\"" D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\"" D_CMND_SSID "\":[\"%s\",\"%s\"],\"" D_CMND_TELEPERIOD "\":%d,\"" D_CMND_SETOPTION "\":[\"%08X\",\"%08X\"]}}"), + Settings.seriallog_level, Settings.weblog_level, Settings.syslog_level, Settings.syslog_host, Settings.syslog_port, Settings.sta_ssid[0], Settings.sta_ssid[1], Settings.tele_period, Settings.flag.data, Settings.flag2.data); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "3")); } @@ -1313,6 +1338,19 @@ void PublishStatus(uint8_t payload) } +void MqttShowPWMState() +{ + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"" D_CMND_PWM "\":{"), mqtt_data); + bool first = true; + for (byte i = 0; i < MAX_PWMS; i++) { + if (pin[GPIO_PWM1 + i] < 99) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s\"" D_CMND_PWM "%d\":%d"), mqtt_data, first ? "" : ",", i+1, Settings.pwm_value[i]); + first = false; + } + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); +} + void MqttShowState() { char stemp1[33]; @@ -1331,6 +1369,11 @@ void MqttShowState() } } + if (pwm_present) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); + MqttShowPWMState(); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_WIFI "\":{\"" D_JSON_AP "\":%d,\"" D_JSON_SSID "\":\"%s\",\"" D_JSON_RSSI "\":%d,\"" D_JSON_APMAC_ADDRESS "\":\"%s\"}}"), mqtt_data, Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.BSSIDstr().c_str()); } @@ -1871,6 +1914,86 @@ void StateLoop() } } +#ifdef USE_ARDUINO_OTA +/*********************************************************************************************\ + * Allow updating via the Arduino OTA-protocol. + * + * - Once started disables current wifi clients and udp + * - Perform restart when done to re-init wifi clients +\*********************************************************************************************/ + +bool arduino_ota_triggered = false; +uint16_t arduino_ota_progress_dot_count = 0; + +void ArduinoOTAInit() +{ + ArduinoOTA.setPort(8266); + ArduinoOTA.setHostname(Settings.hostname); + if (Settings.web_password[0] !=0) ArduinoOTA.setPassword(Settings.web_password); + + ArduinoOTA.onStart([]() + { + SettingsSave(1); // Free flash for OTA update +#ifdef USE_WEBSERVER + if (Settings.webserver) StopWebserver(); +#endif // USE_WEBSERVER +#ifdef USE_ARILUX_RF + AriluxRfDisable(); // Prevent restart exception on Arilux Interrupt routine +#endif // USE_ARILUX_RF + if (Settings.flag.mqtt_enabled) MqttDisconnect(); + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPLOAD "Arduino OTA " D_UPLOAD_STARTED)); + AddLog(LOG_LEVEL_INFO); + arduino_ota_triggered = true; + arduino_ota_progress_dot_count = 0; + delay(100); // Allow time for message xfer + }); + + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) + { + if ((LOG_LEVEL_DEBUG <= seriallog_level)) { + arduino_ota_progress_dot_count++; + Serial.printf("."); + if (!(arduino_ota_progress_dot_count % 80)) Serial.println(); + } + }); + + ArduinoOTA.onError([](ota_error_t error) + { + /* + From ArduinoOTA.h: + typedef enum { OTA_AUTH_ERROR, OTA_BEGIN_ERROR, OTA_CONNECT_ERROR, OTA_RECEIVE_ERROR, OTA_END_ERROR } ota_error_t; + */ + char error_str[100]; + + if ((LOG_LEVEL_DEBUG <= seriallog_level) && arduino_ota_progress_dot_count) Serial.println(); + switch (error) { + case OTA_BEGIN_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_2), sizeof(error_str)); break; + case OTA_RECEIVE_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_5), sizeof(error_str)); break; + case OTA_END_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_7), sizeof(error_str)); break; + default: + snprintf_P(error_str, sizeof(error_str), PSTR(D_UPLOAD_ERROR_CODE " %d"), error); + } + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPLOAD "Arduino OTA %s. " D_RESTARTING), error_str); + AddLog(LOG_LEVEL_INFO); + delay(100); // Allow time for message xfer + ESP.restart(); + }); + + ArduinoOTA.onEnd([]() + { + if ((LOG_LEVEL_DEBUG <= seriallog_level)) Serial.println(); + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPLOAD "Arduino OTA " D_SUCCESSFUL ". " D_RESTARTING)); + AddLog(LOG_LEVEL_INFO); + delay(100); // Allow time for message xfer + ESP.restart(); + }); + + ArduinoOTA.begin(); + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_UPLOAD "Arduino OTA " D_ENABLED " " D_PORT " 8266")); + AddLog(LOG_LEVEL_INFO); +} +#endif // USE_ARDUINO_OTA + /********************************************************************************************/ void SerialInput() @@ -1927,17 +2050,26 @@ void SerialInput() if (serial_in_byte > 127) { // binary data... serial_in_byte_counter = 0; - serial_polling_window = 0; Serial.flush(); return; } - if (isprint(serial_in_byte)) { - if (serial_in_byte_counter < INPUT_BUFFER_SIZE -1) { // add char to string if it still fits - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - serial_polling_window = millis(); - } else { - serial_in_byte_counter = 0; - serial_polling_window = 0; + if (!Settings.flag.mqtt_serial) { + if (isprint(serial_in_byte)) { + if (serial_in_byte_counter < INPUT_BUFFER_SIZE -1) { // add char to string if it still fits + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + } else { + serial_in_byte_counter = 0; + } + } + } else { + if (serial_in_byte) { + if ((serial_in_byte_counter < INPUT_BUFFER_SIZE -1) && (serial_in_byte != Settings.serial_delimiter)) { // add char to string if it still fits + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + serial_polling_window = millis(); + } else { + serial_polling_window = 0; + break; + } } } @@ -1955,24 +2087,7 @@ void SerialInput() } /*-------------------------------------------------------------------------------------------*/ -/* - else if (serial_in_byte == '\n') { - serial_in_buffer[serial_in_byte_counter] = 0; // serial data completed - if (!Settings.flag.mqtt_serial) { - seriallog_level = (Settings.seriallog_level < LOG_LEVEL_INFO) ? (byte)LOG_LEVEL_INFO : Settings.seriallog_level; - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_COMMAND "%s"), serial_in_buffer); - AddLog(LOG_LEVEL_INFO); - ExecuteCommand(serial_in_buffer); - } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"%s\"}"), serial_in_buffer); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED)); - } - serial_in_byte_counter = 0; - serial_polling_window = 0; - Serial.flush(); - return; - } -*/ + else if (!Settings.flag.mqtt_serial && (serial_in_byte == '\n')) { serial_in_buffer[serial_in_byte_counter] = 0; // serial data completed seriallog_level = (Settings.seriallog_level < LOG_LEVEL_INFO) ? (byte)LOG_LEVEL_INFO : Settings.seriallog_level; @@ -1991,8 +2106,6 @@ void SerialInput() snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"%s\"}"), serial_in_buffer); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED)); serial_in_byte_counter = 0; - serial_polling_window = 0; - Serial.flush(); } } @@ -2289,6 +2402,11 @@ void setup() #endif // BE_MINIMAL RtcInit(); + +#ifdef USE_ARDUINO_OTA + ArduinoOTAInit(); +#endif // USE_ARDUINO_OTA + XsnsCall(FUNC_INIT); } @@ -2310,6 +2428,12 @@ void loop() SerialInput(); +#ifdef USE_ARDUINO_OTA + ArduinoOTA.handle(); + // Once OTA is triggered, only handle that and dont do other stuff. (otherwise it fails) + while (arduino_ota_triggered) ArduinoOTA.handle(); +#endif // USE_ARDUINO_OTA + // yield(); // yield == delay(0), delay contains yield, auto yield in loop delay(sleep); // https://github.com/esp8266/Arduino/issues/2021 } diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index 026cc9465..09b4836ba 100644 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -63,6 +63,7 @@ void WifiWpsStatusCallback(wps_cb_status status); #define USE_PMS5003 // Add support for PMS5003 and PMS7003 particle concentration sensor (+1k3 code) #define USE_NOVA_SDS // Add support for SDS011 and SDS021 particle concentration sensor (+0k7 code) #define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) +#define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram) #define USE_IR_HVAC // Support for HVAC system using IR (+2k code) #define USE_IR_RECEIVE // Support for IR receiver (+5k5 code, 264 iram) diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 6a0e2cd97..2fba23102 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -89,6 +89,8 @@ enum UserSelectablePins { GPIO_BACKLIGHT, // Display backlight control GPIO_PMS5003, // Plantower PMS5003 Serial interface GPIO_SDS0X1, // Nova Fitness SDS011 Serial interface + GPIO_SBR_TX, // Serial Bridge Serial interface + GPIO_SBR_RX, // Serial Bridge Serial interface GPIO_SENSOR_END }; // Programmer selectable GPIO functionality offset by user selectable GPIOs @@ -130,7 +132,8 @@ const char kSensorNames[] PROGMEM = D_SENSOR_PZEM_TX "|" D_SENSOR_PZEM_RX "|" D_SENSOR_SAIR_TX "|" D_SENSOR_SAIR_RX "|" D_SENSOR_SPI_CS "|" D_SENSOR_SPI_DC "|" D_SENSOR_BACKLIGHT "|" - D_SENSOR_PMS5003 "|" D_SENSOR_SDS0X1; + D_SENSOR_PMS5003 "|" D_SENSOR_SDS0X1 "|" + D_SENSOR_SBR_TX "|" D_SENSOR_SBR_RX; /********************************************************************************************/ @@ -177,6 +180,7 @@ enum SupportedModules { SONOFF_DUAL_R2, ARILUX_LC06, SONOFF_S31, + ZENGGE_ZF_WF017, MAXMODULE }; /********************************************************************************************/ @@ -231,6 +235,7 @@ const uint8_t kNiceList[MAXMODULE] PROGMEM = { ARILUX_LC01, ARILUX_LC06, ARILUX_LC11, + ZENGGE_ZF_WF017, HUAFAN_SS, KMC_70011, AILIGHT, @@ -786,6 +791,19 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) 0, 0, 0, 0 + }, + { "Zengge WF017", // Zenggee ZJ-WF017-A (ESP12S)) - https://www.ebay.com/p/Smartphone-Android-IOS-WiFi-Music-Controller-for-RGB-5050-3528-LED-Strip-Light/534446632?_trksid=p2047675.l2644 + GPIO_KEY1, // GPIO00 Optional Button + 0, + GPIO_USER, // GPIO02 Empty pad + 0, + GPIO_USER, // GPIO04 W2 - PWM5 + 0, + 0, 0, 0, 0, 0, 0, // Flash connection + GPIO_PWM2, // GPIO12 RGB LED Green + GPIO_PWM1, // GPIO13 RGB LED Red + GPIO_PWM3, // GPIO14 RGB LED Blue + 0, 0, 0 } }; @@ -816,20 +834,6 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, 0 } - { "Zengge WF017", // Zenggee ZJ-WF017-A (ESP12S)) - https://www.ebay.com/p/Smartphone-Android-IOS-WiFi-Music-Controller-for-RGB-5050-3528-LED-Strip-Light/534446632?_trksid=p2047675.l2644 - GPIO_KEY1, // GPIO00 Optional Button - 0, - GPIO_USER, // GPIO02 Empty pad - 0, - GPIO_USER, // GPIO04 W2 - PWM5 - 0, - 0, 0, 0, 0, 0, 0, // Flash connection - GPIO_PWM2, // GPIO12 RGB LED Green - GPIO_PWM1, // GPIO13 RGB LED Red - GPIO_PWM3, // GPIO14 RGB LED Blue - 0, 0, 0 - } - { "SMPW701E", // SM-PW701E WLAN Socket (#1190) 0, 0, 0, 0, GPIO_LED1_INV, // GPIO04 Blue Led (0 = On, 1 = Off) diff --git a/sonoff/support.ino b/sonoff/support.ino index 62312bd7b..6a1293ccf 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -197,6 +197,50 @@ char* dtostrfd(double number, unsigned char prec, char *s) return dtostrf(number, 1, prec, s); } +char* Unescape(char* buffer, uint16_t* size) +{ + uint8_t* read = (uint8_t*)buffer; + uint8_t* write = (uint8_t*)buffer; + uint16_t start_size = *size; + uint16_t end_size = *size; + uint8_t che = 0; + + while (start_size > 0) { + uint8_t ch = *read++; + start_size--; + if (ch != '\\') { + *write++ = ch; + } else { + if (start_size > 0) { + uint8_t chi = *read++; + start_size--; + end_size--; + switch (chi) { + case '\\': che = '\\'; break; // 5C Backslash + case 'a': che = '\a'; break; // 07 Bell (Alert) + case 'b': che = '\b'; break; // 08 Backspace + case 'e': che = '\e'; break; // 1B Escape + case 'f': che = '\f'; break; // 0C Formfeed + case 'n': che = '\n'; break; // 0A Linefeed (Newline) + case 'r': che = '\r'; break; // 0D Carriage return + case 's': che = ' '; break; // 20 Space + case 't': che = '\t'; break; // 09 Horizontal tab + case 'v': che = '\v'; break; // 0B Vertical tab +// case '?': che = '\?'; break; // 3F Question mark + default : { + che = chi; + *write++ = ch; + end_size++; + } + } + *write++ = che; + } + } + } + *size = end_size; + return buffer; +} + boolean ParseIp(uint32_t* addr, const char* str) { uint8_t *part = (uint8_t*)addr; diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 2277c996b..29c790bfa 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -73,6 +73,7 @@ // -- Ota ----------------------------------------- #define OTA_URL "http://sonoff.maddox.co.uk/tasmota/sonoff.ino.bin" // [OtaUrl] +//#define USE_ARDUINO_OTA // Add optional support for Arduino OTA (+4k5 code) /*********************************************************************************************\ * Select ONE of possible MQTT library types below @@ -223,6 +224,7 @@ #define USE_PMS5003 // Add support for PMS5003 and PMS7003 particle concentration sensor (+1k3 code) #define USE_NOVA_SDS // Add support for SDS011 and SDS021 particle concentration sensor (+0k7 code) #define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) +#define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) // -- Low level interface devices ----------------- #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram) diff --git a/sonoff/xdrv_01_light.ino b/sonoff/xdrv_01_light.ino index f5f6184c7..54c7cebc9 100644 --- a/sonoff/xdrv_01_light.ino +++ b/sonoff/xdrv_01_light.ino @@ -55,10 +55,12 @@ enum LightCommands { CMND_COLOR, CMND_COLORTEMPERATURE, CMND_DIMMER, CMND_LED, CMND_LEDTABLE, CMND_FADE, - CMND_PIXELS, CMND_ROTATION, CMND_SCHEME, CMND_SPEED, CMND_WAKEUP, CMND_WAKEUPDURATION, CMND_WIDTH, CMND_UNDOCA }; + CMND_PIXELS, CMND_ROTATION, CMND_SCHEME, CMND_SPEED, CMND_WAKEUP, CMND_WAKEUPDURATION, + CMND_WIDTH, CMND_CHANNEL, CMND_HSBCOLOR, CMND_UNDOCA }; const char kLightCommands[] PROGMEM = D_CMND_COLOR "|" D_CMND_COLORTEMPERATURE "|" D_CMND_DIMMER "|" D_CMND_LED "|" D_CMND_LEDTABLE "|" D_CMND_FADE "|" - D_CMND_PIXELS "|" D_CMND_ROTATION "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|" D_CMND_WIDTH "|UNDOCA" ; + D_CMND_PIXELS "|" D_CMND_ROTATION "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|" + D_CMND_WIDTH "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR "|UNDOCA" ; struct LRgbColor { uint8_t R, G, B; @@ -535,6 +537,8 @@ void LightState(uint8_t append) { char scolor[25]; char scommand[33]; + float hsb[3]; + int16_t h,s,b; if (append) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); @@ -546,6 +550,19 @@ void LightState(uint8_t append) mqtt_data, scommand, GetStateText(light_power), Settings.light_dimmer); if (light_subtype > LST_SINGLE) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_CMND_COLOR "\":\"%s\""), mqtt_data, LightGetColor(0, scolor)); + // Add status for HSB + LightGetHsb(&hsb[0],&hsb[1],&hsb[2]); + // Scale these percentages up to the numbers expected byt he client + h = round(hsb[0] * 360); + s = round(hsb[1] * 100); + b = round(hsb[2] * 100); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_CMND_HSBCOLOR "\":\"%d,%d,%d\""), mqtt_data, h,s,b); + // Add status for each channel + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_CMND_CHANNEL "\":[" ), mqtt_data); + for (byte i = 0; i < light_subtype; i++) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%s%d" ), mqtt_data, (i > 0 ? "," : ""), round(light_current_color[i]/2.55)); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s]" ), mqtt_data); } if ((LST_COLDWARM == light_subtype) || (LST_RGBWC == light_subtype)) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_CMND_COLORTEMPERATURE "\":%d"), mqtt_data, LightGetColorTemp()); @@ -1053,6 +1070,47 @@ boolean LightCommand() snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, scolor); } } + else if ((CMND_CHANNEL == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= light_subtype ) ) { + // Set "Channel" directly - this allows Color and Direct PWM control to coexist + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + uint8_t level = XdrvMailbox.payload; + light_current_color[XdrvMailbox.index-1] = round(level * 2.55); + LightSetColor(); + coldim = true; + } + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_NVALUE, command, XdrvMailbox.index, round(light_current_color[XdrvMailbox.index -1] / 2.55)); + } + else if ((CMND_HSBCOLOR == command_code) && ( light_subtype >= LST_RGB)) { + // Implement method to "direct set" color by HSB (HSB is passed comma separated, 0360) ? (HSB[0] % 360) : HSB[0] ) /360.0, + ( (HSB[1]>100) ? (HSB[1] % 100) : HSB[1] ) /100.0, + ( (HSB[2]>100) ? (HSB[2] % 100) : HSB[2] ) /100.0, + 0); + } else { + LightState(0); + } + } #ifdef USE_WS2812 // *********************************************************************** else if ((CMND_LED == command_code) && (LT_WS2812 == light_type) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= Settings.light_pixels)) { if (XdrvMailbox.data_len > 0) { diff --git a/sonoff/xdrv_08_serial_bridge.ino b/sonoff/xdrv_08_serial_bridge.ino new file mode 100644 index 000000000..428dc5e35 --- /dev/null +++ b/sonoff/xdrv_08_serial_bridge.ino @@ -0,0 +1,149 @@ +/* + xdrv_08_serial_bridge.ino - serial bridge support for Sonoff-Tasmota + + Copyright (C) 2018 Theo Arends and Dániel Zoltán Tolnai + + 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 . +*/ + +#ifdef USE_SERIAL_BRIDGE +/*********************************************************************************************\ + * Serial Bridge using Software Serial library (TasmotaSerial) +\*********************************************************************************************/ +#define SERIAL_BRIDGE_BUFFER_SIZE 130 + +#include + +enum SerialBridgeCommands { CMND_SSERIALSEND, CMND_SBAUDRATE }; +const char kSerialBridgeCommands[] PROGMEM = D_CMND_SSERIALSEND "|" D_CMND_SBAUDRATE; + +TasmotaSerial *SerialBridgeSerial; + +uint8_t serial_bridge_active = 1; +uint8_t serial_bridge_in_byte_counter = 0; +unsigned long serial_bridge_polling_window = 0; +char serial_bridge_buffer[SERIAL_BRIDGE_BUFFER_SIZE]; + +void SerialBridgeInput() +{ + while (SerialBridgeSerial->available()) { + yield(); + uint8_t serial_in_byte = SerialBridgeSerial->read(); + + if (serial_in_byte > 127) { // binary data... + serial_bridge_in_byte_counter = 0; + SerialBridgeSerial->flush(); + return; + } + if (serial_in_byte) { + if ((serial_in_byte_counter < sizeof(serial_bridge_buffer) -1) && (serial_in_byte != Settings.serial_delimiter)) { // add char to string if it still fits + serial_bridge_buffer[serial_bridge_in_byte_counter++] = serial_in_byte; + serial_bridge_polling_window = millis(); // Wait for more data + } else { + serial_bridge_polling_window = 0; // Publish now + break; + } + } + } + + if (serial_bridge_in_byte_counter && (millis() > (serial_bridge_polling_window + SERIAL_POLLING))) { + serial_bridge_buffer[serial_bridge_in_byte_counter] = 0; // serial data completed + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SSERIALRECEIVED "\":\"%s\"}"), serial_bridge_buffer); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SSERIALRECEIVED)); + serial_bridge_in_byte_counter = 0; + } +} + +/********************************************************************************************/ + +void SerialBridgeInit(void) +{ + serial_bridge_active = 0; + if ((pin[GPIO_SBR_RX] < 99) && (pin[GPIO_SBR_TX] < 99)) { + SerialBridgeSerial = new TasmotaSerial(pin[GPIO_SBR_RX], pin[GPIO_SBR_TX]); + if (SerialBridgeSerial->begin(Settings.sbaudrate * 1200)) { // Baud rate is stored div 1200 so it fits into one byte + serial_bridge_active = 1; + SerialBridgeSerial->flush(); + } + } +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +boolean SerialBridgeCommand() +{ + char command [CMDSZ]; + boolean serviced = true; + + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kSerialBridgeCommands); + if ((CMND_SSERIALSEND == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) { + if (XdrvMailbox.data_len > 0) { + if (1 == XdrvMailbox.index) { + SerialBridgeSerial->write(XdrvMailbox.data, XdrvMailbox.data_len); + SerialBridgeSerial->write("\n"); + } + else if (2 == XdrvMailbox.index) { + SerialBridgeSerial->write(XdrvMailbox.data, XdrvMailbox.data_len); + } + else if (3 == XdrvMailbox.index) { + SerialBridgeSerial->write(Unescape(XdrvMailbox.data, &XdrvMailbox.data_len), XdrvMailbox.data_len); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); + } + } + else if (CMND_SBAUDRATE == command_code) { + char *p; + int baud = strtol(XdrvMailbox.data, &p, 10); + if (baud > 0) { + baud /= 1200; // Make it a valid baudrate + Settings.sbaudrate = (1 == XdrvMailbox.payload) ? SOFT_BAUDRATE / 1200 : baud; + SerialBridgeSerial->begin(Settings.sbaudrate * 1200); // Reinitialize serial port with new baud rate + } + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_LVALUE, command, Settings.sbaudrate * 1200); + } + else { + serviced = false; // Unknown command + } + return serviced; +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +#define XDRV_08 + +boolean Xdrv08(byte function) +{ + boolean result = false; + + if (serial_bridge_active) { + switch (function) { + case FUNC_INIT: + SerialBridgeInit(); + break; + case FUNC_LOOP: + SerialBridgeInput(); + break; + case FUNC_COMMAND: + result = SerialBridgeCommand(); + break; + } + } + return result; +} + +#endif // USE_SERIAL_BRIDGE